libxpertmass-1.4.0/.gitignore000664 001750 001750 00000000141 15100504560 017421 0ustar00rusconirusconi000000 000000 TODO .cache .kdev4/ compile_commands.json *.kdev4 maintainer-scripts/ _clang-format __pycache__ libxpertmass-1.4.0/AUTHORS000664 001750 001750 00000000046 15100504560 016505 0ustar00rusconirusconi000000 000000 Filippo Rusconi libxpertmass-1.4.0/CMakeLists.txt000664 001750 001750 00000022016 15100506565 020205 0ustar00rusconirusconi000000 000000 # ############################################################################## # ############################################################################## # CMake configuration 3.18.4 in oldstable as of 2024 - 07 - 15 cmake_minimum_required(VERSION 3.18.4) set(CMAKE_PROJECT_INCLUDE_BEFORE ${CMAKE_SOURCE_DIR}/CMakeStuff/ancillary-project-include-before.cmake) # ############################################################################## # ############################################################################## # Basic information about project project( XpertMass VERSION 1.4.0 DESCRIPTION "Libraries [XpertMass::Core (non-GUI) and XpertMass::Gui) for the MsXpertSuite project." HOMEPAGE_URL "http://wwww.msxpertsuite.org/libxpertmass" LANGUAGES CXX) set(LOWCASE_PROJECT_NAME xpertmass) # project(VERSION) above sets: PROJECT_VERSION, _VERSION # PROJECT_VERSION_MAJOR, _VERSION_MAJOR PROJECT_VERSION_MINOR, # _VERSION_MINOR PROJECT_VERSION_PATCH, # _VERSION_PATCH message("\n${BoldGreen}Building version ${PROJECT_VERSION} of \ ${PROJECT_NAME} (${CMAKE_PROJECT_DESCRIPTION})${ColourReset}\n") # Set additional project information set(COMPANY "MsXpertSuite.org") set(COPYRIGHT "Copyright (c) 2008-2025 Filippo Rusconi. Licensed under GPLv3+") set(IDENTIFIER "org.msxpertsuite.libxpertmass") # This line MUST be located prior to include'ing GNUInstallDirs !!! ############################################################# ############################################################# # Installation prefix if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Install path" FORCE) else() set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Install path" FORCE) message(STATUS "Setting CMAKE_INSTALL_PREFIX to ${CMAKE_INSTALL_PREFIX}") endif() include(GNUInstallDirs) include(FeatureSummary) # Add folder where are supportive functions set(CMAKE_UTILS_PATH ${CMAKE_SOURCE_DIR}/CMakeStuff) set(CMAKE_MODULE_PATH ${CMAKE_UTILS_PATH}/modules) set(CMAKE_TOOLCHAINS_PATH ${CMAKE_UTILS_PATH}/toolchains) # Our build scripts take for granted that this files is available. configure_file(${CMAKE_UTILS_PATH}/version.cmake.in ${CMAKE_BINARY_DIR}/version.txt) set(HOME_DEVEL_DIR $ENV{HOME}/devel) message("\n${BoldYellow}The devel directory where all the development projects \ should reside: ${HOME_DEVEL_DIR}.${ColourReset}\n") set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_VERBOSE_MAKEFILE ON) message( "\n${BoldGreen}Configuring build for project ${CMAKE_PROJECT_NAME}${ColourReset}\n" ) # This export will allow using the flags to be used by the Clang-based code # analyzer. set(CMAKE_EXPORT_COMPILE_COMMANDS 1) if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json") execute_process( COMMAND cmake -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json) endif() # We want C++17 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) message(STATUS "CMAKE_CXX_STANDARD: ${CMAKE_CXX_STANDARD}") message(STATUS "CMAKE_CXX_STANDARD_REQUIRED: ${CMAKE_CXX_STANDARD_REQUIRED}") message(STATUS "CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}") # ############################################################################## # Setting command line compiler definitions: message( STATUS "${BoldYellow}Setting compiler command line definitions.${ColourReset}" ) # We do not want warnings for unknown pragmas: message("Setting definition -Wno-unknown-pragmas.") add_definitions(-Wno-unknown-pragmas) # Enable warnings and possibly treat them as errors message("Setting definition -Wall -pedantic.") add_definitions(-Wall -pedantic) message("Setting definition -Wextra.") add_definitions(-Wextra) if(WARN_AS_ERROR) message("Setting definition -Werror.") add_definitions(-Werror) else() message("NOT setting definition -Werror.") endif() message(STATUS "\nCMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}\n") option(MXE "Use the cross-compiling environment MXE" OFF) option(PAPPSO_LOCAL_DEV "Use locally developped libpappsomspp" OFF) option(PAPPSO_LOCAL_INST "Use /usr/local/lib installed libpappsomspp" OFF) if(PAPPSO_LOCAL_DEV AND PAPPSO_LOCAL_INST) message(FATAL_ERROR "Cannot have both PAPPSO_LOCAL_DEV and PAPPSO_LOCAL_INST") endif() message(STATUS "PAPPSO_LOCAL_DEV: ${PAPPSO_LOCAL_DEV}") message(STATUS "PAPPSO_LOCAL_INST: ${PAPPSO_LOCAL_INST}") # ############################################################################## # ############################################################################## # Platform-specific CMake configuration if(WIN32 OR _WIN32) if(MXE) # Run the following cmake command line: x86_64-w64-mingw32.shared-cmake # -DCMAKE_BUILD_TYPE=Release -DMXE=1 ../../development include(${CMAKE_TOOLCHAINS_PATH}/mxe-toolchain.cmake) # Set the name to the systemUname variable because in this situation that # name is not found, it it passed as a flag in the command line. set(SYSTEM_UNAME_S "mxe") elseif(WIN10MINGW64) include(${CMAKE_TOOLCHAINS_PATH}/win10-mingw64-toolchain.cmake) endif() elseif(UNIX) message("UNIX toolchain being selected") include(${CMAKE_TOOLCHAINS_PATH}/unix-toolchain-common.cmake) endif() message("") message( STATUS "${BoldGreen}Starting configuration of ${CMAKE_PROJECT_NAME}${ColourReset}") message("") message( STATUS "${BoldYellow}The build toolchain is: ${SYSTEM_UNAME_S}${ColourReset}") message("") # ############################################################################## # ############################################################################## # Essential software configuration message(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR}) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build, options are: None, Debug, Release, RelWithDebInfo, MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE MATCHES "Release") message(STATUS "Compiling in release mode.") add_definitions("-DQT_NO_DEBUG_OUTPUT") endif() if(CMAKE_BUILD_TYPE MATCHES "Debug") message(STATUS "Compiling in debug mode.") message(STATUS "Add definition -ggdb3 to format debug output for GDB.") add_definitions(-ggdb3) endif() if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") message(STATUS "Compiling in release with debug info mode.") endif(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") message( STATUS "${BoldYellow}CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}.${ColourReset}") if(PROFILE) message( STATUS "${BoldYellow}Profiling is requested, adding -pg flag.${ColourReset}" ) add_definitions(-pg) endif() message( STATUS "${BoldYellow}Main CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}${ColourReset}" ) message( STATUS "${BoldYellow}CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}${ColourReset}") # ############################################################################## # ############################################################################## # BUILD OF THE LIBRARIES message(STATUS "\n${BoldGreen}Adding subdirectory src for PROJECT: \ ${CMAKE_PROJECT_NAME}${ColourReset}\n") add_subdirectory(source) # ############################################################################## # ############################################################################## # BUILD THE DOCUMENTATION (JavaScript reference and/or devdoc) add_subdirectory(doc) # ############################################################################## # ############################################################################## # BUILD OF THE TESTS option(BUILD_TESTS OFF) option(CODE_COVERAGE "Collect coverage for tests " OFF) if(UNIX AND BUILD_TESTS) if(CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE) include(CodeCoverage) append_coverage_compiler_flags() endif() message(STATUS "\n${BoldGreen}Build the tests ${ColourReset}\n") message("Adding subdirectory tests for PROJECT: ${CMAKE_PROJECT_NAME}") add_subdirectory(tests) endif() # ############################################################################## # ############################################################################## # Creation of the source archive include(${CMAKE_UTILS_PATH}/targz-source-package-creation.cmake) set(PARENT_DIR ${CMAKE_SOURCE_DIR}/..) set(TARBALL_DIR ${PARENT_DIR}/tarballs) add_custom_target( archive cpack -G TGZ --config CPackSourceConfig.cmake && mv ${CMAKE_BINARY_DIR}/${ARCHIVE_FILE_NAME_EXT} ${TARBALL_DIR} && ln -sf ${TARBALL_DIR}/${ARCHIVE_FILE_NAME_EXT} ${PARENT_DIR}/${DEBIAN_ORIG_FILE_NAME} && ln -sf ${TARBALL_DIR}/${ARCHIVE_FILE_NAME_EXT} ${TARBALL_DIR}/${DEBIAN_ORIG_FILE_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMENT "Creating .tar.gz" VERBATIM) # -----------------------------------------------------------# # Summary # -----------------------------------------------------------# message("\n\nBegin feature summary") message("---------------------\n") feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES WHAT ALL) message("---------------------") message("End feature summary\n\n") libxpertmass-1.4.0/CMakeStuff/000775 001750 001750 00000000000 15100507137 017430 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/CMakeStuff/.targz-source-package-creation.cmake.swp000664 001750 001750 00000030000 15100504560 027126 0ustar00rusconirusconi000000 000000 b0VIM 9.1=e"Y3(rusconiroma~rusconi/devel/minexpert2/development/CMakeStuff/targz-source-package-creation.cmakeutf-8 3210#"! Utp#ad, #?d< O - u ] V 5 )    include(CPainclude(CPack)setinclude(CPack)set(CPACK_VERBATIM_VARIABLES YES) minexpert2-doc.pdf) libmassgui.a libmass.a doc/user-manual/DC-user-manual TODO compile_commands.json .*kdev.* maintainer-scripts/ /\\.kdev4/ \\.git/ .cache/set(CPACK_SOURCE_IGNORE_FILESset(CPACK_SOURCE_PACKAGE_FILE_NAME "${LOWCASE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")set(CPACK_SOURCE_GENERATOR "TGZ")set(CPACK_RESOURCE_FILE_AUTHORS "${CMAKE_SOURCE_DIR}/AUTHORS")set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Software for visualization and exploration of mass spectrometric data")set(CPACK_PACKAGE_VERSION "${VERSION}")set(CPACK_PACKAGE_VENDOR "msXpertSuite")set(CPACK_PACKAGE_NAME "minexpert2")# it is listed here.# possible that it be absent from source dir! Which is why# generated right while doing 'cmake archive', so it is not# in the tarball. However, because it is a configure_file it is # this is a generated file (configure_file) that needs not be# Listed below is the doc/user-manual/DC-user-manual:# Packagerlibxpertmass-1.4.0/CMakeStuff/XpertMassConfig.cmake.in000664 001750 001750 00000000446 15100504560 024114 0ustar00rusconirusconi000000 000000 @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/XpertMassCoreStaticTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/XpertMassCoreSharedTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/XpertMassGuiStaticTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/XpertMassGuiSharedTargets.cmake") libxpertmass-1.4.0/CMakeStuff/ancillary-project-include-before.cmake000664 001750 001750 00000001153 15100504560 026732 0ustar00rusconirusconi000000 000000 # Add folder where are supportive functions set(CMAKE_UTILS_PATH ${CMAKE_SOURCE_DIR}/CMakeStuff) set(CMAKE_TOOLCHAINS_PATH ${CMAKE_UTILS_PATH}/toolchains) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeStuff/modules" ${CMAKE_MODULE_PATH}) set(CMAKE_PREFIX_PATH "${CMAKE_MODULE_PATH}" ${CMAKE_PREFIX_PATH}) # Include the system's uname that fills in SYSTEM_UNAME_S. # Sets WIN32 if SYSTEM_UNAME_S is "^.*MING64.*" # Note that WIN32 is set even on 64 bits systems. include(${CMAKE_UTILS_PATH}/systemUname.cmake) # Include the various colors we want to use in the output include(${CMAKE_UTILS_PATH}/outputColors.cmake) libxpertmass-1.4.0/CMakeStuff/modules/000775 001750 001750 00000000000 15100507137 021100 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/CMakeStuff/modules/Catch.cmake000664 001750 001750 00000035457 15100504560 023137 0ustar00rusconirusconi000000 000000 # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. #[=======================================================================[.rst: Catch ----- This module defines a function to help use the Catch test framework. The :command:`catch_discover_tests` discovers tests by asking the compiled test executable to enumerate its tests. This does not require CMake to be re-run when tests change. However, it may not work in a cross-compiling environment, and setting test properties is less convenient. This command is intended to replace use of :command:`add_test` to register tests, and will create a separate CTest test for each Catch test case. Note that this is in some cases less efficient, as common set-up and tear-down logic cannot be shared by multiple test cases executing in the same instance. However, it provides more fine-grained pass/fail information to CTest, which is usually considered as more beneficial. By default, the CTest test name is the same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. .. command:: catch_discover_tests Automatically add tests with CTest by querying the compiled test executable for available tests:: catch_discover_tests(target [TEST_SPEC arg1...] [EXTRA_ARGS arg1...] [WORKING_DIRECTORY dir] [TEST_PREFIX prefix] [TEST_SUFFIX suffix] [PROPERTIES name1 value1...] [TEST_LIST var] [REPORTER reporter] [OUTPUT_DIR dir] [OUTPUT_PREFIX prefix] [OUTPUT_SUFFIX suffix] [DISCOVERY_MODE ] ) ``catch_discover_tests`` sets up a post-build command on the test executable that generates the list of tests by parsing the output from running the test with the ``--list-test-names-only`` argument. This ensures that the full list of tests is obtained. Since test discovery occurs at build time, it is not necessary to re-run CMake when the list of tests changes. However, it requires that :prop_tgt:`CROSSCOMPILING_EMULATOR` is properly set in order to function in a cross-compiling environment. Additionally, setting properties on tests is somewhat less convenient, since the tests are not available at CMake time. Additional test properties may be assigned to the set of tests as a whole using the ``PROPERTIES`` option. If more fine-grained test control is needed, custom content may be provided through an external CTest script using the :prop_dir:`TEST_INCLUDE_FILES` directory property. The set of discovered tests is made accessible to such a script via the ``_TESTS`` variable. The options are: ``target`` Specifies the Catch executable, which must be a known CMake executable target. CMake will substitute the location of the built executable when running the test. ``TEST_SPEC arg1...`` Specifies test cases, wildcarded test cases, tags and tag expressions to pass to the Catch executable with the ``--list-test-names-only`` argument. ``EXTRA_ARGS arg1...`` Any extra arguments to pass on the command line to each test case. ``WORKING_DIRECTORY dir`` Specifies the directory in which to run the discovered test cases. If this option is not provided, the current binary directory is used. ``TEST_PREFIX prefix`` Specifies a ``prefix`` to be prepended to the name of each discovered test case. This can be useful when the same test executable is being used in multiple calls to ``catch_discover_tests()`` but with different ``TEST_SPEC`` or ``EXTRA_ARGS``. ``TEST_SUFFIX suffix`` Similar to ``TEST_PREFIX`` except the ``suffix`` is appended to the name of every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may be specified. ``PROPERTIES name1 value1...`` Specifies additional properties to be set on all tests discovered by this invocation of ``catch_discover_tests``. ``TEST_LIST var`` Make the list of tests available in the variable ``var``, rather than the default ``_TESTS``. This can be useful when the same test executable is being used in multiple calls to ``catch_discover_tests()``. Note that this variable is only available in CTest. ``REPORTER reporter`` Use the specified reporter when running the test case. The reporter will be passed to the Catch executable as ``--reporter reporter``. ``OUTPUT_DIR dir`` If specified, the parameter is passed along as ``--out dir/`` to Catch executable. The actual file name is the same as the test name. This should be used instead of ``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output when using parallel test execution. ``OUTPUT_PREFIX prefix`` May be used in conjunction with ``OUTPUT_DIR``. If specified, ``prefix`` is added to each output file name, like so ``--out dir/prefix``. ``OUTPUT_SUFFIX suffix`` May be used in conjunction with ``OUTPUT_DIR``. If specified, ``suffix`` is added to each output file name, like so ``--out dir/suffix``. This can be used to add a file extension to the output e.g. ".xml". ``DL_PATHS path...`` Specifies paths that need to be set for the dynamic linker to find shared libraries/DLLs when running the test executable (PATH/LD_LIBRARY_PATH respectively). These paths will both be set when retrieving the list of test cases from the test executable and when the tests are executed themselves. This requires cmake/ctest >= 3.22. ``DL_FRAMEWORK_PATHS path...`` Specifies paths that need to be set for the dynamic linker to find libraries packaged as frameworks on Apple platforms when running the test executable (DYLD_FRAMEWORK_PATH). These paths will both be set when retrieving the list of test cases from the test executable and when the tests are executed themselves. This requires cmake/ctest >= 3.22. `DISCOVERY_MODE mode`` Provides control over when ``catch_discover_tests`` performs test discovery. By default, ``POST_BUILD`` sets up a post-build command to perform test discovery at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD`` behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until just prior to test execution. This way test discovery occurs in the target environment where the test has a better chance at finding appropriate runtime dependencies. ``DISCOVERY_MODE`` defaults to the value of the ``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when calling ``catch_discover_tests``. This provides a mechanism for globally selecting a preferred test discovery behavior without having to modify each call site. #]=======================================================================] #------------------------------------------------------------------------------ function(catch_discover_tests TARGET) cmake_parse_arguments( "" "" "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE" "TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS;DL_FRAMEWORK_PATHS" ${ARGN} ) if(NOT _WORKING_DIRECTORY) set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() if(NOT _TEST_LIST) set(_TEST_LIST ${TARGET}_TESTS) endif() if(_DL_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22") endif() if(_DL_FRAMEWORK_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") message(FATAL_ERROR "The DL_FRAMEWORK_PATHS option requires at least cmake 3.22") endif() if(NOT _DISCOVERY_MODE) if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE) set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") endif() set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE}) endif() if (NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$") message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") endif() ## Generate a unique name based on the extra arguments string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}") string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}") set(ctest_include_file "${ctest_file_base}_include.cmake") set(ctest_tests_file "${ctest_file_base}_tests.cmake") get_property(crosscompiling_emulator TARGET ${TARGET} PROPERTY CROSSCOMPILING_EMULATOR ) if(_DISCOVERY_MODE STREQUAL "POST_BUILD") add_custom_command( TARGET ${TARGET} POST_BUILD BYPRODUCTS "${ctest_tests_file}" COMMAND "${CMAKE_COMMAND}" -D "TEST_TARGET=${TARGET}" -D "TEST_EXECUTABLE=$" -D "TEST_EXECUTOR=${crosscompiling_emulator}" -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" -D "TEST_SPEC=${_TEST_SPEC}" -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" -D "TEST_PROPERTIES=${_PROPERTIES}" -D "TEST_PREFIX=${_TEST_PREFIX}" -D "TEST_SUFFIX=${_TEST_SUFFIX}" -D "TEST_LIST=${_TEST_LIST}" -D "TEST_REPORTER=${_REPORTER}" -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" -D "TEST_DL_PATHS=${_DL_PATHS}" -D "TEST_DL_FRAMEWORK_PATHS=${_DL_FRAMEWORK_PATHS}" -D "CTEST_FILE=${ctest_tests_file}" -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" VERBATIM ) file(WRITE "${ctest_include_file}" "if(EXISTS \"${ctest_tests_file}\")\n" " include(\"${ctest_tests_file}\")\n" "else()\n" " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" "endif()\n" ) elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG ) if(GENERATOR_IS_MULTI_CONFIG) set(ctest_tests_file "${ctest_file_base}_tests-$.cmake") endif() string(CONCAT ctest_include_content "if(EXISTS \"$\")" "\n" " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$\" OR\n" " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" " include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n" " catch_discover_tests_impl(" "\n" " TEST_EXECUTABLE" " [==[" "$" "]==]" "\n" " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" " TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n" " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" " TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n" " TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n" " TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n" " TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n" " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" " TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n" " TEST_DL_FRAMEWORK_PATHS" " [==[" "${_DL_FRAMEWORK_PATHS}" "]==]" "\n" " CTEST_FILE" " [==[" "${CTEST_FILE}" "]==]" "\n" " )" "\n" " endif()" "\n" " include(\"${ctest_tests_file}\")" "\n" "else()" "\n" " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" "endif()" "\n" ) if(GENERATOR_IS_MULTI_CONFIG) foreach(_config ${CMAKE_CONFIGURATION_TYPES}) file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $) endforeach() string(CONCAT ctest_include_multi_content "if(NOT CTEST_CONFIGURATION_TYPE)" "\n" " message(\"No configuration for testing specified, use '-C '.\")" "\n" "else()" "\n" " include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n" "endif()" "\n" ) file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}") else() file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") endif() endif() if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") # Add discovered tests to directory TEST_INCLUDE_FILES set_property(DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" ) else() # Add discovered tests as directory TEST_INCLUDE_FILE if possible get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET) if (NOT ${test_include_file_set}) set_property(DIRECTORY PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" ) else() message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE") endif() endif() endfunction() ############################################################################### set(_CATCH_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file" ) libxpertmass-1.4.0/CMakeStuff/modules/CatchAddTests.cmake000664 001750 001750 00000014744 15100504560 024567 0ustar00rusconirusconi000000 000000 # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. function(add_command NAME) set(_args "") # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments math(EXPR _last_arg ${ARGC}-1) foreach(_n RANGE 1 ${_last_arg}) set(_arg "${ARGV${_n}}") if(_arg MATCHES "[^-./:a-zA-Z0-9_]") set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument else() set(_args "${_args} ${_arg}") endif() endforeach() set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) endfunction() function(catch_discover_tests_impl) cmake_parse_arguments( "" "" "TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE" "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR;TEST_DL_PATHS;TEST_DL_FRAMEWORK_PATHS" ${ARGN} ) set(prefix "${_TEST_PREFIX}") set(suffix "${_TEST_SUFFIX}") set(spec ${_TEST_SPEC}) set(extra_args ${_TEST_EXTRA_ARGS}) set(properties ${_TEST_PROPERTIES}) set(reporter ${_TEST_REPORTER}) set(output_dir ${_TEST_OUTPUT_DIR}) set(output_prefix ${_TEST_OUTPUT_PREFIX}) set(output_suffix ${_TEST_OUTPUT_SUFFIX}) set(dl_paths ${_TEST_DL_PATHS}) set(dl_framework_paths ${_TEST_DL_FRAMEWORK_PATHS}) set(script) set(suite) set(tests) if(WIN32) set(dl_paths_variable_name PATH) elseif(APPLE) set(dl_paths_variable_name DYLD_LIBRARY_PATH) else() set(dl_paths_variable_name LD_LIBRARY_PATH) endif() # Run test executable to get list of available tests if(NOT EXISTS "${_TEST_EXECUTABLE}") message(FATAL_ERROR "Specified test executable '${_TEST_EXECUTABLE}' does not exist" ) endif() if(dl_paths) cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths) set(ENV{${dl_paths_variable_name}} "${paths}") endif() if(APPLE AND dl_framework_paths) cmake_path(CONVERT "${dl_framework_paths}" TO_NATIVE_PATH_LIST paths) set(ENV{DYLD_FRAMEWORK_PATH} "${paths}") endif() execute_process( COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet OUTPUT_VARIABLE output RESULT_VARIABLE result WORKING_DIRECTORY "${_TEST_WORKING_DIR}" ) if(NOT ${result} EQUAL 0) message(FATAL_ERROR "Error running test executable '${_TEST_EXECUTABLE}':\n" " Result: ${result}\n" " Output: ${output}\n" ) endif() # Make sure to escape ; (semicolons) in test names first, because # that'd break the foreach loop for "Parse output" later and create # wrongly splitted and thus failing test cases (false positives) string(REPLACE ";" "\;" output "${output}") string(REPLACE "\n" ";" output "${output}") # Prepare reporter if(reporter) set(reporter_arg "--reporter ${reporter}") # Run test executable to check whether reporter is available # note that the use of --list-reporters is not the important part, # we only want to check whether the execution succeeds with ${reporter_arg} execute_process( COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters OUTPUT_VARIABLE reporter_check_output RESULT_VARIABLE reporter_check_result WORKING_DIRECTORY "${_TEST_WORKING_DIR}" ) if(${reporter_check_result} EQUAL 255) message(FATAL_ERROR "\"${reporter}\" is not a valid reporter!\n" ) elseif(NOT ${reporter_check_result} EQUAL 0) message(FATAL_ERROR "Error running test executable '${_TEST_EXECUTABLE}':\n" " Result: ${reporter_check_result}\n" " Output: ${reporter_check_output}\n" ) endif() endif() # Prepare output dir if(output_dir AND NOT IS_ABSOLUTE ${output_dir}) set(output_dir "${_TEST_WORKING_DIR}/${output_dir}") if(NOT EXISTS ${output_dir}) file(MAKE_DIRECTORY ${output_dir}) endif() endif() if(dl_paths) foreach(path ${dl_paths}) cmake_path(NATIVE_PATH path native_path) list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}") endforeach() endif() if(APPLE AND dl_framework_paths) foreach(path ${dl_framework_paths}) cmake_path(NATIVE_PATH path native_path) list(APPEND environment_modifications "DYLD_FRAMEWORK_PATH=path_list_prepend:${native_path}") endforeach() endif() # Parse output foreach(line ${output}) set(test "${line}") # Escape characters in test case names that would be parsed by Catch2 # Note that the \ escaping must happen FIRST! Do not change the order. set(test_name "${test}") foreach(char \\ , [ ]) string(REPLACE ${char} "\\${char}" test_name "${test_name}") endforeach(char) # ...add output dir if(output_dir) string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean "${test_name}") set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}") endif() # ...and add to script add_command(add_test "${prefix}${test}${suffix}" ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" "${test_name}" ${extra_args} "${reporter_arg}" "${output_dir_arg}" ) add_command(set_tests_properties "${prefix}${test}${suffix}" PROPERTIES WORKING_DIRECTORY "${_TEST_WORKING_DIR}" ${properties} ) if(environment_modifications) add_command(set_tests_properties "${prefix}${test}${suffix}" PROPERTIES ENVIRONMENT_MODIFICATION "${environment_modifications}") endif() list(APPEND tests "${prefix}${test}${suffix}") endforeach() # Create a list of all discovered tests, which users may use to e.g. set # properties on the tests add_command(set ${_TEST_LIST} ${tests}) # Write CTest script file(WRITE "${_CTEST_FILE}" "${script}") endfunction() if(CMAKE_SCRIPT_MODE_FILE) catch_discover_tests_impl( TEST_EXECUTABLE ${TEST_EXECUTABLE} TEST_EXECUTOR ${TEST_EXECUTOR} TEST_WORKING_DIR ${TEST_WORKING_DIR} TEST_SPEC ${TEST_SPEC} TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} TEST_PROPERTIES ${TEST_PROPERTIES} TEST_PREFIX ${TEST_PREFIX} TEST_SUFFIX ${TEST_SUFFIX} TEST_LIST ${TEST_LIST} TEST_REPORTER ${TEST_REPORTER} TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR} TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX} TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX} TEST_DL_PATHS ${TEST_DL_PATHS} TEST_DL_FRAMEWORK_PATHS ${TEST_DL_FRAMEWORK_PATHS} CTEST_FILE ${CTEST_FILE} ) endif() libxpertmass-1.4.0/CMakeStuff/modules/CodeCoverage.cmake000664 001750 001750 00000075177 15100504560 024446 0ustar00rusconirusconi000000 000000 # Copyright (c) 2012 - 2017, Lars Bilke # All rights reserved. # # Redistribution and use in source and binary forms, with or without 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. # # 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. # # 3. Neither the name of the copyright holder nor the names of its contributors # 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 THE COPYRIGHT HOLDER 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. # # CHANGES: # # 2012-01-31, Lars Bilke # - Enable Code Coverage # # 2013-09-17, Joakim Söderberg # - Added support for Clang. # - Some additional usage instructions. # # 2016-02-03, Lars Bilke # - Refactored functions to use named parameters # # 2017-06-02, Lars Bilke # - Merged with modified version from github.com/ufz/ogs # # 2019-05-06, Anatolii Kurotych # - Remove unnecessary --coverage flag # # 2019-12-13, FeRD (Frank Dana) # - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor # of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. # - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY # - All setup functions: accept BASE_DIRECTORY, EXCLUDE list # - Set lcov basedir with -b argument # - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be # overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) # - Delete output dir, .info file on 'make clean' # - Remove Python detection, since version mismatches will break gcovr # - Minor cleanup (lowercase function names, update examples...) # # 2019-12-19, FeRD (Frank Dana) # - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets # # 2020-01-19, Bob Apthorpe # - Added gfortran support # # 2020-02-17, FeRD (Frank Dana) # - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters # in EXCLUDEs, and remove manual escaping from gcovr targets # # 2021-01-19, Robin Mueller # - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run # - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional # flags to the gcovr command # # 2020-05-04, Mihchael Davis # - Add -fprofile-abs-path to make gcno files contain absolute paths # - Fix BASE_DIRECTORY not working when defined # - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines # # 2021-05-10, Martin Stump # - Check if the generator is multi-config before warning about non-Debug builds # # 2022-02-22, Marko Wehle # - Change gcovr output from -o for --xml and --html output respectively. # This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". # # 2022-09-28, Sebastian Mueller # - fix append_coverage_compiler_flags_to_target to correctly add flags # - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent) # # USAGE: # # 1. Copy this file into your cmake modules path. # # 2. Add the following line to your CMakeLists.txt (best inside an if-condition # using a CMake option() to enable it just optionally): # include(CodeCoverage) # # 3. Append necessary compiler flags for all supported source files: # append_coverage_compiler_flags() # Or for specific target: # append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) # # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og # # 4. If you need to exclude additional directories from the report, specify them # using full paths in the COVERAGE_EXCLUDES variable before calling # setup_target_for_coverage_*(). # Example: # set(COVERAGE_EXCLUDES # '${PROJECT_SOURCE_DIR}/src/dir1/*' # '/path/to/my/src/dir2/*') # Or, use the EXCLUDE argument to setup_target_for_coverage_*(). # Example: # setup_target_for_coverage_lcov( # NAME coverage # EXECUTABLE testrunner # EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") # # 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set # relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) # Example: # set(COVERAGE_EXCLUDES "dir1/*") # setup_target_for_coverage_gcovr_html( # NAME coverage # EXECUTABLE testrunner # BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" # EXCLUDE "dir2/*") # # 5. Use the functions described below to create a custom make target which # runs your test executable and produces a code coverage report. # # 6. Build a Debug build: # cmake -DCMAKE_BUILD_TYPE=Debug .. # make # make my_coverage_target # include(CMakeParseArguments) option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) # Check prereqs find_program( GCOV_PATH gcov ) find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) find_program( CPPFILT_PATH NAMES c++filt ) if(NOT GCOV_PATH) message(FATAL_ERROR "gcov not found! Aborting...") endif() # NOT GCOV_PATH # Check supported compiler (Clang, GNU and Flang) get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach(LANG ${LANGUAGES}) if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") endif() elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang") message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...") endif() endforeach() set(COVERAGE_COMPILER_FLAGS "-g --coverage" CACHE INTERNAL "") if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCXXCompilerFlag) check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path) if(HAVE_cxx_fprofile_abs_path) set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") endif() endif() if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCCompilerFlag) check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path) if(HAVE_c_fprofile_abs_path) set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") endif() endif() set(CMAKE_Fortran_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the Fortran compiler during coverage builds." FORCE ) set(CMAKE_CXX_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE ) set(CMAKE_C_FLAGS_COVERAGE ${COVERAGE_COMPILER_FLAGS} CACHE STRING "Flags used by the C compiler during coverage builds." FORCE ) set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used for linking binaries during coverage builds." FORCE ) set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "" CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE ) mark_as_advanced( CMAKE_Fortran_FLAGS_COVERAGE CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") link_libraries(gcov) endif() # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_lcov( # NAME testrunner_coverage # New target name # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES testrunner # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative # # to BASE_DIRECTORY, with CMake 3.4+) # NO_DEMANGLE # Don't demangle C++ symbols # # even if c++filt is found # ) function(setup_target_for_coverage_lcov) set(options NO_DEMANGLE SONARQUBE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT LCOV_PATH) message(FATAL_ERROR "lcov not found! Aborting...") endif() # NOT LCOV_PATH if(NOT GENHTML_PATH) message(FATAL_ERROR "genhtml not found! Aborting...") endif() # NOT GENHTML_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (CMake 3.4+: Also compute absolute paths) set(LCOV_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) endif() list(APPEND LCOV_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES LCOV_EXCLUDES) # Conditional arguments if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) set(GENHTML_EXTRA_ARGS "--demangle-cpp") endif() # Setting up commands which will be run to generate coverage data. # Cleanup lcov set(LCOV_CLEAN_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters ) # Create baseline to make sure untouched files show up in the report set(LCOV_BASELINE_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base ) # Run tests set(LCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} ) # Capturing lcov counters and generating report set(LCOV_CAPTURE_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture ) # add baseline counters set(LCOV_BASELINE_COUNT_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total ) # filter collected data to final coverage report set(LCOV_FILTER_CMD ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info ) # Generate HTML output set(LCOV_GEN_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info ) if(${Coverage_SONARQUBE}) # Generate SonarQube output set(GCOVR_XML_CMD ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) set(GCOVR_XML_CMD_COMMAND COMMAND ${GCOVR_XML_CMD} ) set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml) set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.") endif() if(CODE_COVERAGE_VERBOSE) message(STATUS "Executed command report") message(STATUS "Command to clean up lcov: ") string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") message(STATUS "${LCOV_CLEAN_CMD_SPACED}") message(STATUS "Command to create baseline: ") string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") message(STATUS "${LCOV_BASELINE_CMD_SPACED}") message(STATUS "Command to run the tests: ") string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") message(STATUS "Command to capture counters and generate report: ") string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") message(STATUS "Command to add baseline counters: ") string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") message(STATUS "Command to filter collected data: ") string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") message(STATUS "${LCOV_FILTER_CMD_SPACED}") message(STATUS "Command to generate lcov HTML output: ") string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") if(${Coverage_SONARQUBE}) message(STATUS "Command to generate SonarQube XML output: ") string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") message(STATUS "${GCOVR_XML_CMD_SPACED}") endif() endif() # Setup target add_custom_target(${Coverage_NAME} COMMAND ${LCOV_CLEAN_CMD} COMMAND ${LCOV_BASELINE_CMD} COMMAND ${LCOV_EXEC_TESTS_CMD} COMMAND ${LCOV_CAPTURE_CMD} COMMAND ${LCOV_BASELINE_COUNT_CMD} COMMAND ${LCOV_FILTER_CMD} COMMAND ${LCOV_GEN_HTML_CMD} ${GCOVR_XML_CMD_COMMAND} # Set output files as GENERATED (will be removed on 'make clean') BYPRODUCTS ${Coverage_NAME}.base ${Coverage_NAME}.capture ${Coverage_NAME}.total ${Coverage_NAME}.info ${GCOVR_XML_CMD_BYPRODUCTS} ${Coverage_NAME}/index.html WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." ) # Show where to find the lcov info report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." ${GCOVR_XML_CMD_COMMENT} ) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." ) endfunction() # setup_target_for_coverage_lcov # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_gcovr_xml( # NAME ctest_coverage # New target name # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES executable_target # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative # # to BASE_DIRECTORY, with CMake 3.4+) # ) # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the # GCVOR command. function(setup_target_for_coverage_gcovr_xml) set(options NONE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT GCOVR_PATH) message(FATAL_ERROR "gcovr not found! Aborting...") endif() # NOT GCOVR_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (CMake 3.4+: Also compute absolute paths) set(GCOVR_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) endif() list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES GCOVR_EXCLUDES) # Combine excludes to several -e arguments set(GCOVR_EXCLUDE_ARGS "") foreach(EXCLUDE ${GCOVR_EXCLUDES}) list(APPEND GCOVR_EXCLUDE_ARGS "-e") list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") endforeach() # Set up commands which will be run to generate coverage data # Run tests set(GCOVR_XML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} ) # Running gcovr set(GCOVR_XML_CMD ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) if(CODE_COVERAGE_VERBOSE) message(STATUS "Executed command report") message(STATUS "Command to run tests: ") string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") message(STATUS "Command to generate gcovr XML coverage data: ") string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") message(STATUS "${GCOVR_XML_CMD_SPACED}") endif() add_custom_target(${Coverage_NAME} COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} COMMAND ${GCOVR_XML_CMD} BYPRODUCTS ${Coverage_NAME}.xml WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Running gcovr to produce Cobertura code coverage report." ) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." ) endfunction() # setup_target_for_coverage_gcovr_xml # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_gcovr_html( # NAME ctest_coverage # New target name # EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES executable_target # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative # # to BASE_DIRECTORY, with CMake 3.4+) # ) # The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the # GCVOR command. function(setup_target_for_coverage_gcovr_html) set(options NONE) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT GCOVR_PATH) message(FATAL_ERROR "gcovr not found! Aborting...") endif() # NOT GCOVR_PATH # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(DEFINED Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (CMake 3.4+: Also compute absolute paths) set(GCOVR_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) if(CMAKE_VERSION VERSION_GREATER 3.4) get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) endif() list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES GCOVR_EXCLUDES) # Combine excludes to several -e arguments set(GCOVR_EXCLUDE_ARGS "") foreach(EXCLUDE ${GCOVR_EXCLUDES}) list(APPEND GCOVR_EXCLUDE_ARGS "-e") list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") endforeach() # Set up commands which will be run to generate coverage data # Run tests set(GCOVR_HTML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} ) # Create folder set(GCOVR_HTML_FOLDER_CMD ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} ) # Running gcovr set(GCOVR_HTML_CMD ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) if(CODE_COVERAGE_VERBOSE) message(STATUS "Executed command report") message(STATUS "Command to run tests: ") string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") message(STATUS "Command to create a folder: ") string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") message(STATUS "Command to generate gcovr HTML coverage data: ") string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") message(STATUS "${GCOVR_HTML_CMD_SPACED}") endif() add_custom_target(${Coverage_NAME} COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} COMMAND ${GCOVR_HTML_FOLDER_CMD} COMMAND ${GCOVR_HTML_CMD} BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Running gcovr to produce HTML code coverage report." ) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ; COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." ) endfunction() # setup_target_for_coverage_gcovr_html # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # # setup_target_for_coverage_fastcov( # NAME testrunner_coverage # New target name # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES testrunner # Dependencies to build first # BASE_DIRECTORY "../" # Base directory for report # # (defaults to PROJECT_SOURCE_DIR) # EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. # NO_DEMANGLE # Don't demangle C++ symbols # # even if c++filt is found # SKIP_HTML # Don't create html report # POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths # ) function(setup_target_for_coverage_fastcov) set(options NO_DEMANGLE SKIP_HTML) set(oneValueArgs BASE_DIRECTORY NAME) set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD) cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT FASTCOV_PATH) message(FATAL_ERROR "fastcov not found! Aborting...") endif() if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH) message(FATAL_ERROR "genhtml not found! Aborting...") endif() # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR if(Coverage_BASE_DIRECTORY) get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) else() set(BASEDIR ${PROJECT_SOURCE_DIR}) endif() # Collect excludes (Patterns, not paths, for fastcov) set(FASTCOV_EXCLUDES "") foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") endforeach() list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) # Conditional arguments if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) set(GENHTML_EXTRA_ARGS "--demangle-cpp") endif() # Set up commands which will be run to generate coverage data set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} --search-directory ${BASEDIR} --process-gcno --output ${Coverage_NAME}.json --exclude ${FASTCOV_EXCLUDES} ) set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info ) if(Coverage_SKIP_HTML) set(FASTCOV_HTML_CMD ";") else() set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info ) endif() set(FASTCOV_POST_CMD ";") if(Coverage_POST_CMD) set(FASTCOV_POST_CMD ${Coverage_POST_CMD}) endif() if(CODE_COVERAGE_VERBOSE) message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") message(" Running tests:") string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") message(" Capturing fastcov counters and generating report:") string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") message(" ${FASTCOV_CAPTURE_CMD_SPACED}") message(" Converting fastcov .json to lcov .info:") string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}") message(" ${FASTCOV_CONVERT_CMD_SPACED}") if(NOT Coverage_SKIP_HTML) message(" Generating HTML report: ") string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") message(" ${FASTCOV_HTML_CMD_SPACED}") endif() if(Coverage_POST_CMD) message(" Running post command: ") string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}") message(" ${FASTCOV_POST_CMD_SPACED}") endif() endif() # Setup target add_custom_target(${Coverage_NAME} # Cleanup fastcov COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} --search-directory ${BASEDIR} --zerocounters COMMAND ${FASTCOV_EXEC_TESTS_CMD} COMMAND ${FASTCOV_CAPTURE_CMD} COMMAND ${FASTCOV_CONVERT_CMD} COMMAND ${FASTCOV_HTML_CMD} COMMAND ${FASTCOV_POST_CMD} # Set output files as GENERATED (will be removed on 'make clean') BYPRODUCTS ${Coverage_NAME}.info ${Coverage_NAME}.json ${Coverage_NAME}/index.html # report directory WORKING_DIRECTORY ${PROJECT_BINARY_DIR} DEPENDS ${Coverage_DEPENDENCIES} VERBATIM # Protect arguments to commands COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." ) set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.") if(NOT Coverage_SKIP_HTML) string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") endif() # Show where to find the fastcov info report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} ) endfunction() # setup_target_for_coverage_fastcov function(append_coverage_compiler_flags) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") endfunction() # append_coverage_compiler_flags # Setup coverage for specific library function(append_coverage_compiler_flags_to_target name) separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") target_compile_options(${name} PRIVATE ${_flag_list}) if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") target_link_libraries(${name} PRIVATE gcov) endif() endfunction() libxpertmass-1.4.0/CMakeStuff/modules/FindQScintilla.cmake000664 001750 001750 00000004145 15100504560 024747 0ustar00rusconirusconi000000 000000 # FindQScintilla.cmake cmake_minimum_required(VERSION 3.16) find_package(Qt6 QUIET COMPONENTS Core) message(STATUS "Now looking for the QScintilla library") # Find include directory find_path(QSCINTILLA_INCLUDE_DIR NAMES qsciscintilla.h PATHS /usr/include/Qsci /usr/include/qt6/Qsci /usr/include/${CMAKE_LIBRARY_ARCHITECTURE}/Qsci /usr/include/${CMAKE_LIBRARY_ARCHITECTURE}/qt6/Qsci /usr/local/include/Qsci /usr/local/include/qt6/Qsci /usr/local/include/${CMAKE_LIBRARY_ARCHITECTURE}/Qsci /usr/local/include/${CMAKE_LIBRARY_ARCHITECTURE}/qt6/Qsci PATH_SUFFIXES Qsci ) message(STATUS "The libqscintilla2-qt6 include dir: ${QSCINTILLA_INCLUDE_DIR}") # Find library with version-specific search set(_qscintilla_lib_names) list(APPEND _qscintilla_lib_names qscintilla2_qt6 libqscintilla2_qt6 ) # Add fallback names list(APPEND _qscintilla_lib_names qscintilla2 libqscintilla2 ) find_library(QSCINTILLA_LIBRARY NAMES ${_qscintilla_lib_names} HINTS /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/local/lib/${CMAKE_LIBRARY_ARCHITECTURE} /usr/lib /usr/local/lib ) # Handle found components include(FindPackageHandleStandardArgs) find_package_handle_standard_args(QScintilla REQUIRED_VARS QSCINTILLA_LIBRARY QSCINTILLA_INCLUDE_DIR VERSION_VAR QSCINTILLA_VERSION_STRING ) if(QSCINTILLA_FOUND) set(QSCINTILLA_INCLUDE_DIRS ${QSCINTILLA_INCLUDE_DIR}) set(QSCINTILLA_LIBRARIES ${QSCINTILLA_LIBRARY}) # Create imported target if(NOT TARGET QScintilla::QScintilla) add_library(QScintilla::QScintilla UNKNOWN IMPORTED) set_target_properties(QScintilla::QScintilla PROPERTIES IMPORTED_LOCATION "${QSCINTILLA_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${QSCINTILLA_INCLUDE_DIRS}" # This is to tell the build process we want to import the lib symbols. INTERFACE_COMPILE_DEFINITIONS "QSCINTILLA_DLL" ) # Link against required Qt components find_package(Qt6 REQUIRED COMPONENTS Core Widgets) set_target_properties(QScintilla::QScintilla PROPERTIES INTERFACE_LINK_LIBRARIES "Qt6::Core;Qt6::Widgets" ) endif() endif() libxpertmass-1.4.0/CMakeStuff/modules/FindSQLite3.cmake000664 001750 001750 00000001624 15100504560 024127 0ustar00rusconirusconi000000 000000 find_path(SQLite3_INCLUDE_DIR sqlite3.h PATHS /usr/local/include /usr/include) find_library(SQLite3_LIBRARY NAMES sqlite3 HINTS /usr/lib /usr/local/lib) if(SQLite3_INCLUDE_DIR AND SQLite3_LIBRARY) mark_as_advanced(SQLite3_INCLUDE_DIR) mark_as_advanced(SQLite3_LIBRARY) set(SQLite3_FOUND TRUE) endif() if(SQLite3_FOUND) if(NOT SQLite3_FIND_QUIETLY) message(STATUS "Found SQLite3_LIBRARY: ${SQLite3_LIBRARY}") endif() if(NOT TARGET SQLite3::SQLite3) add_library(SQLite3::SQLite3 UNKNOWN IMPORTED) set_target_properties(SQLite3::SQLite3 PROPERTIES IMPORTED_LOCATION "${SQLite3_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}") endif() else() if(SQLite3_FIND_REQUIRED) message(FATAL_ERROR "Could not find libsqlite3. Please do specify the SQLite3_INCLUDE_DIR and SQLite3_LIBRARY variables using cmake!") endif() endif() libxpertmass-1.4.0/CMakeStuff/modules/FindZstd.cmake000664 001750 001750 00000001516 15100504560 023627 0ustar00rusconirusconi000000 000000 find_path(Zstd_INCLUDE_DIRS zstd.h PATHS /usr/local/include /usr/include) find_library(Zstd_LIBRARY NAMES zstd HINTS /usr/lib /usr/local/lib) if(Zstd_INCLUDE_DIRS AND Zstd_LIBRARY) mark_as_advanced(Zstd_INCLUDE_DIRS) mark_as_advanced(Zstd_LIBRARY) set(Zstd_FOUND TRUE) endif() if(Zstd_FOUND) if(NOT Zstd_FIND_QUIETLY) message(STATUS "Found Zstd_LIBRARY: ${Zstd_LIBRARY}") endif() if(NOT TARGET Zstd::Zstd) add_library(Zstd::Zstd UNKNOWN IMPORTED) set_target_properties(Zstd::Zstd PROPERTIES IMPORTED_LOCATION "${Zstd_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${Zstd_INCLUDE_DIRS}") endif() else() if(Zstd_FIND_REQUIRED) message(FATAL_ERROR "Could not find libzstd. Please do specify the Zstd_INCLUDE_DIRS and Zstd_LIBRARY variables using cmake!") endif() endif() libxpertmass-1.4.0/CMakeStuff/outputColors.cmake000664 001750 001750 00000001065 15100504560 023153 0ustar00rusconirusconi000000 000000 # We want to use some colors for the message output. string(ASCII 27 Esc) set(ColourReset "${Esc}[m") set(ColourBold "${Esc}[1m") set(Red "${Esc}[31m") set(Green "${Esc}[32m") set(Yellow "${Esc}[33m") set(Blue "${Esc}[34m") set(Magenta "${Esc}[35m") set(Cyan "${Esc}[36m") set(White "${Esc}[37m") set(BoldRed "${Esc}[1;31m") set(BoldGreen "${Esc}[1;32m") set(BoldYellow "${Esc}[1;33m") set(BoldBlue "${Esc}[1;34m") set(BoldMagenta "${Esc}[1;35m") set(BoldCyan "${Esc}[1;36m") set(BoldWhite "${Esc}[1;37m") libxpertmass-1.4.0/CMakeStuff/src-config.h.cmake.in000664 001750 001750 00000000164 15100504560 023315 0ustar00rusconirusconi000000 000000 #pragma once // #pragma message "Including the src config.h file from the binary dir." #cmakedefine MXE "@MXE@" libxpertmass-1.4.0/CMakeStuff/systemUname.cmake000664 001750 001750 00000001576 15100504560 022752 0ustar00rusconirusconi000000 000000 # Ask that uname -s be performed and store the value in SYSTEM_UNAME_S for # later reference. macro(get_uname_string) execute_process(COMMAND uname -s OUTPUT_VARIABLE SYSTEM_UNAME_S) if(${SYSTEM_UNAME_S} MATCHES "^.*MINGW64.*") message(STATUS "System detected as Windows10, setting WIN32 AND WIN10MINGW64") # Note that WIN32 is set even on 64 bits systems. set(WIN32 1) set(WIN10MINGW64 1) #else() #message(STATUS "System is not Windows.") endif() endmacro() get_uname_string() macro(get_deb_triplet_string OUTPUT_VAR) find_program(DPKG_CMD dpkg) if(DPKG_CMD) execute_process( COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH OUTPUT_VARIABLE DEB_MULTI_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE ) message(STATUS "Got the triplet: ${DEB_MULTI_ARCH}") set(${OUTPUT_VAR} ${DEB_MULTI_ARCH}) endif() endmacro() libxpertmass-1.4.0/CMakeStuff/targz-source-package-creation.cmake000664 001750 001750 00000002320 15100504560 026244 0ustar00rusconirusconi000000 000000 # Packager # Listed below is the doc/user-manual/DC-user-manual: # this is a generated file (configure_file) that needs not be # in the tarball. However, because it is a configure_file it is # generated right while doing 'cmake archive', so it is not # possible that it be absent from source dir! Which is why # it is listed here. set(CPACK_PACKAGE_NAME "libxpertmass") set(CPACK_PACKAGE_VENDOR "MsXpertSuite") set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Libraries for mass spectrometry") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_AUTHORS "${CMAKE_SOURCE_DIR}/AUTHORS") set(CPACK_SOURCE_GENERATOR "TGZ") # Handy short names set(ARCHIVE_FILE_NAME "lib${LOWCASE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") set(DEBIAN_ORIG_FILE_NAME "lib${LOWCASE_PROJECT_NAME}_${CPACK_PACKAGE_VERSION}.orig.tar.gz") # The required name set(CPACK_SOURCE_PACKAGE_FILE_NAME ${ARCHIVE_FILE_NAME}) # Short name with the extension set(ARCHIVE_FILE_NAME_EXT ${ARCHIVE_FILE_NAME}.tar.gz) set(CPACK_SOURCE_IGNORE_FILES debian/ .cache/ \\.git/ /\\.kdev4/ .*kdev.* compile_commands.json TODO doc/html) set(CPACK_VERBATIM_VARIABLES YES) include(CPack) libxpertmass-1.4.0/CMakeStuff/tests-config.h.cmake.in000664 001750 001750 00000000246 15100504560 023671 0ustar00rusconirusconi000000 000000 #pragma once #include #define TESTS_INPUT_DIR "@CMAKE_CURRENT_LIST_DIR@/data" #define TESTS_OUTPUT_DIR "@CMAKE_CURRENT_BINARY_DIR@/data" libxpertmass-1.4.0/CMakeStuff/toolchains/000775 001750 001750 00000000000 15100507137 021573 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/CMakeStuff/toolchains/mxe-toolchain.cmake000664 001750 001750 00000015651 15100504560 025351 0ustar00rusconirusconi000000 000000 # This file should be included if the command line reads like this: # x86_64-w64-mingw32.shared-cmake -DMXE=1 ... MESSAGE("MXE (M cross environment) https://mxe.cc/") message("Please run the configuration like this:") message("x86_64-w64-mingw32.shared-cmake -G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Release ../../development") set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES ${HOME_DEVEL_DIR}/mxe/usr/x86_64-w64-mingw32.shared/include) set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES ${HOME_DEVEL_DIR}/mxe/usr/x86_64-w64-mingw32.shared/include) set(MXE_ROOT_DIR "${HOME_DEVEL_DIR}/mxe") set(MXE_SHIPPED_DLLS_DIR "${MXE_ROOT_DIR}/dll-set-for-packages") # We are building the lib, so need to export the symbols. # Also we want the code to know that we are building for MXE # because there are occasional #ifdef for code that must be # adapted (older Qt libs in MXE) add_definitions(-DEXPORT_LIB_SYMBOLS -DMXE) find_package( Qt6 COMPONENTS Core Svg Xml Network PrintSupport Qml Sql Concurrent Core5Compat REQUIRED ) set(IsoSpec++_FOUND 1) set(IsoSpec++_INCLUDE_DIRS "${HOME_DEVEL_DIR}/isospec/development") set(IsoSpec++_LIBRARIES "${MXE_SHIPPED_DLLS_DIR}/libIsoSpec++.dll") if(NOT TARGET IsoSpec++::IsoSpec++) add_library(IsoSpec++::IsoSpec++ UNKNOWN IMPORTED) set_target_properties(IsoSpec++::IsoSpec++ PROPERTIES IMPORTED_LOCATION "${IsoSpec++_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${IsoSpec++_INCLUDE_DIRS}") endif() set(PappsoMSpp_FOUND 1) set(PappsoMSpp_INCLUDE_DIRS "${HOME_DEVEL_DIR}/pappsomspp/development/src") set(PappsoMSpp_LIBRARIES "${MXE_SHIPPED_DLLS_DIR}/libpappsomspp-core.dll") if(NOT TARGET PappsoMSpp::Core) add_library(PappsoMSpp::Core UNKNOWN IMPORTED GLOBAL) set_target_properties(PappsoMSpp::Core PROPERTIES IMPORTED_LOCATION ${PappsoMSpp_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS}) endif() set(PappsoMSppGui_FOUND 1) set(PappsoMSppGui_LIBRARIES "${MXE_SHIPPED_DLLS_DIR}/libpappsomspp-gui.dll") if(NOT TARGET PappsoMSpp::Gui) add_library(PappsoMSpp::Gui UNKNOWN IMPORTED GLOBAL) set_target_properties(PappsoMSpp::Gui PROPERTIES IMPORTED_LOCATION ${PappsoMSppGui_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS}) endif() include_directories(${include_directories} ${PappsoMSpp_INCLUDE_DIRS} ${PappsoMSpp_INCLUDE_DIRS}) find_package(ZLIB REQUIRED) find_package(SQLite3 REQUIRED) find_package( Boost COMPONENTS chrono container filesystem iostreams thread REQUIRED ) find_package(Catch2) message("Catch2 major version found: " ${Catch2_VERSION_MAJOR}) add_compile_definitions(CATCH2_MAJOR_VERSION_${Catch2_VERSION_MAJOR}) set(QCustomPlotQt6_FOUND 1) set(QCustomPlotQt6_INCLUDE_DIR "${HOME_DEVEL_DIR}/qcustomplot/development") set(QCustomPlotQt6_LIBRARIES "${HOME_DEVEL_DIR}/qcustomplot/build-area/mxe/libQCustomPlotQt6.dll") if(NOT TARGET QCustomPlotQt6::QCustomPlotQt6) add_library(QCustomPlotQt6::QCustomPlotQt6 UNKNOWN IMPORTED) set_target_properties( QCustomPlotQt6::QCustomPlotQt6 PROPERTIES IMPORTED_LOCATION "${QCustomPlotQt6_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${QCustomPlotQt6_INCLUDE_DIR}" INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY ) endif() # Unfortunately no CMake configuration file is available. # find_package(QUAZIP REQUIRED) set(QUAZIP_FOUND 1) set(QUAZIP_INCLUDE_DIR "${HOME_DEVEL_DIR}/quazip/development/quazip") set(QUAZIP_LIBRARIES "${HOME_DEVEL_DIR}/quazip/build-area/mxe/quazip/libquazip1-qt6.dll") set(QUAZIP_ZLIB_INCLUDE_DIR ${ZLIB_INCLUDE_DIRS}) set(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR}) message(STATUS "QUAZIP_INCLUDE_DIR :${QUAZIP_INCLUDE_DIR}") if(NOT TARGET QuaZip::QuaZip) add_library(QuaZip::QuaZip UNKNOWN IMPORTED) set_target_properties( QuaZip::QuaZip PROPERTIES IMPORTED_LOCATION ${QUAZIP_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${QUAZIP_INCLUDE_DIR} ) endif() set(OdsStream_FOUND 1) set(OdsStream_INCLUDE_DIR "${HOME_DEVEL_DIR}/odsstream/development/src") set(OdsStream_LIBRARIES "${HOME_DEVEL_DIR}/odsstream/build-area/mxe/src/libodsstream.dll") if(NOT TARGET OdsStream::Core) add_library(OdsStream::Core UNKNOWN IMPORTED) set_target_properties( OdsStream::Core PROPERTIES IMPORTED_LOCATION "${OdsStream_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${OdsStream_INCLUDE_DIR}" ) endif() find_package(Zstd REQUIRED) set(PwizLite_FOUND 1) set(PwizLite_INCLUDE_DIRS "${HOME_DEVEL_DIR}/pwizlite/development/src") set(PwizLite_LIBRARIES "${HOME_DEVEL_DIR}/pwizlite/build-area/mxe/src/libpwizlite.dll") if(NOT TARGET PwizLite::PwizLite) add_library(PwizLite::PwizLite UNKNOWN IMPORTED) set_target_properties( PwizLite::PwizLite PROPERTIES IMPORTED_LOCATION "${PwizLite_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${PwizLite_INCLUDE_DIRS}" ) endif() set(liblzf_FOUND 1) set(liblzf_INCLUDE_DIRS "${HOME_DEVEL_DIR}/lzf/development") set(liblzf_LIBRARIES "${HOME_DEVEL_DIR}/lzf/build-area/mxe/liblzf.dll") if(NOT TARGET liblzf::liblzf) add_library(liblzf::liblzf UNKNOWN IMPORTED) set_target_properties( liblzf::liblzf PROPERTIES IMPORTED_LOCATION "${liblzf_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${liblzf_INCLUDE_DIRS}" INTERFACE_COMPILE_DEFINITIONS LIBLZF_USE_LIBRARY ) endif() set(QScintilla_FOUND 1) set(QScintilla_INCLUDE_DIRS "${HOME_DEVEL_DIR}/qscintilla2/development/src") set(QScintilla_LIBRARIES "${HOME_DEVEL_DIR}/qscintilla2/build-area/mxe/src/libqscintilla2_qt6.dll") if(NOT TARGET QScintilla::QScintilla) add_library(QScintilla::QScintilla UNKNOWN IMPORTED) set_target_properties(QScintilla::QScintilla PROPERTIES IMPORTED_LOCATION "${QScintilla_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${QScintilla_INCLUDE_DIRS}") endif() #OPENMP message(STATUS "${BoldYellow}OpenMP support is compulsory.${ColourReset}") #message(STATUS "CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") #set(OpenMP_DIR ${CMAKE_MODULE_PATH}) find_package(OpenMP REQUIRED) set(CORE_SOURCE_LIB ${CMAKE_BINARY_DIR}/source/XpertMassCore/libXpertMassCore.dll) set(GUI_SOURCE_LIB ${CMAKE_BINARY_DIR}/source/XpertMassGui/libXpertMassGui.dll) ## We can build the package setup executable with this specific command. add_custom_target(dllinstall COMMAND ${CMAKE_COMMAND} -E copy ${CORE_SOURCE_LIB} ${GUI_SOURCE_LIB} ${MXE_SHIPPED_DLLS_DIR} COMMENT "Build and copy the dll files to their MXE destination." DEPENDS ${CORE_SOURCE_LIB} ${GUI_SOURCE_LIB} VERBATIM) # On Win10 all the code is relocatable. remove_definitions(-fPIC) libxpertmass-1.4.0/CMakeStuff/toolchains/unix-toolchain-common.cmake000664 001750 001750 00000014607 15100504560 027031 0ustar00rusconirusconi000000 000000 message(STATUS "UNIX toolchain (non APPLE)") set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) set(LINKER_FLAGS "-Wl,--no-as-needed") find_package( Qt6 COMPONENTS Core Svg Xml Network PrintSupport Qml Sql Concurrent Core5Compat REQUIRED ) find_package(IsoSpec++ REQUIRED) find_package(PwizLite REQUIRED) find_package( Boost COMPONENTS chrono container filesystem iostreams thread REQUIRED ) find_package(QCustomPlotQt6 REQUIRED) if(SQLite3_FOUND) else(SQLite3_FOUND) set(SQLite3_DIR ${CMAKE_MODULE_PATH}) find_package(SQLite3 REQUIRED) endif(SQLite3_FOUND) find_package(ZLIB REQUIRED) find_package(Zstd REQUIRED) find_package(liblzf REQUIRED) find_package(OdsStream COMPONENTS Core) find_package(QuaZip-Qt6 REQUIRED) find_package(QScintilla REQUIRED) if(PAPPSO_LOCAL_INST) message(STATUS "Selecting the .local pappso libs install setup.") include(${CMAKE_TOOLCHAINS_PATH}/unix-toolchain-local-inst-pappso.cmake) elseif(PAPPSO_LOCAL_DEV) message(STATUS "Selecting the local pappso lib development setup.") include(${CMAKE_TOOLCHAINS_PATH}/unix-toolchain-local-dev-pappso.cmake) else() find_package( PappsoMSpp COMPONENTS Core Gui GLOBAL REQUIRED ) endif() #OPENMP message(STATUS "${BoldYellow}OpenMP support is compulsory.${ColourReset}") #message(STATUS "CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") #set(OpenMP_DIR ${CMAKE_MODULE_PATH}) find_package(OpenMP REQUIRED) #We know this one is empty: #message(STATUS "OpenMP found with include dirs: ${OpenMP_CXX_INCLUDE_DIRS}") #Which is why we will reconstruct it for gcc #on the basis of the path to the libgomp.so library.The include #directory is nothing more than the path to that library with '/include' #added to it.This is what is done below. message(STATUS "OpenMP found with libraries: ${OpenMP_CXX_LIBRARIES}") # On mingw64 with ucrt64 environment, the include file is # /c/msys64/ucrt64/lib/gcc/x86_64-w64-mingw32/13.2.0/include/omp.h if(OpenMP_CXX_INCLUDE_DIRS STREQUAL "") message(STATUS "OpenMP include directories could not be found. Crafting them manually starting from the library directories.") if(CMAKE_COMPILER_IS_GNUCXX) message(STATUS "The compiler is CMAKE_COMPILER_IS_GNUCXX") #We now need to extract the libgomp.so library path, and craft the #include dir path which is replacing libgomp.so with include. set(OpenMP_CXX_INCLUDE_DIRS ${OpenMP_CXX_LIBRARIES}) list(FILTER OpenMP_CXX_INCLUDE_DIRS INCLUDE REGEX ".*libgomp.*") #message(STATUS "Started crafting OpenMP include dirs: ${OpenMP_CXX_INCLUDE_DIRS}") list(LENGTH OpenMP_CXX_INCLUDE_DIRS INCLUDE_DIRS_LIST_LENGTH) #message(STATUS "Remaining items in OpenMP_CXX_INCLUDE_DIRS: ${INCLUDE_DIRS_LIST_LENGTH}") list(GET OpenMP_CXX_INCLUDE_DIRS 0 OpenMP_CXX_INCLUDE_DIR) #message(STATUS "Started crafting OpenMP include dir: ${OpenMP_CXX_INCLUDE_DIR}") string(REGEX REPLACE "libgomp.so.*" "include" OpenMP_CXX_INCLUDE_SINGLE_DIR ${OpenMP_CXX_INCLUDE_DIR}) message(STATUS "OpenMP single include dir: ${OpenMP_CXX_INCLUDE_SINGLE_DIR}") if(EXISTS "${OpenMP_CXX_INCLUDE_SINGLE_DIR}") set(OpenMP_CXX_INCLUDE_DIRS ${OpenMP_CXX_INCLUDE_SINGLE_DIR}) message(STATUS "OpenMP found with include dirs: ${OpenMP_CXX_INCLUDE_DIRS}") else() set(OpenMP_CXX_INCLUDE_DIRS "") message(STATUS "OpenMP found but not the include directories.") endif() else() # Compiler is not CMAKE_COMPILER_IS_GNUCXX # Check if the compiler is clang++ #set(DEBUG_COMPILER "/usr/bin/clang") #string(REGEX MATCH ".*clang\\+\\+.*" CMAKE_CXX_COMPILER_IS_CLANG ${DEBUG_COMPILER}) string(REGEX MATCH ".*clang\\+\\+.*" CMAKE_CXX_COMPILER_IS_CLANG ${CMAKE_CXX_COMPILER}) #string(FIND ${CMAKE_CXX_COMPILER} "clang++" CMAKE_CXX_COMPILER_IS_CLANG) if(CMAKE_CXX_COMPILER_IS_CLANG STREQUAL "") message(STATUS "Failed to identify compiler different than g++ and clang++") else() message(STATUS "The compiler has been identified as clang++: ${CMAKE_CXX_COMPILER}") execute_process ( COMMAND bash -c "clang --print-resource-dir" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE CLANG_RESOURCE_DIR ) if(${CLANG_RESOURCE_DIR} STREQUAL "") message(STATUS "Failed to determine the Clang resource dir") else() #message(STATUS "Clang resource dir: \"${CLANG_RESOURCE_DIR}\"") set(CLANG_RESOURCE_DIR "${CLANG_RESOURCE_DIR}/include") #message(STATUS "Clang resource dir: ${CLANG_RESOURCE_DIR}") if(EXISTS "${CLANG_RESOURCE_DIR}") set(OpenMP_CXX_INCLUDE_DIRS ${CLANG_RESOURCE_DIR}) message(STATUS "OpenMP found with include dirs: ${OpenMP_CXX_INCLUDE_DIRS}") else() set(OpenMP_CXX_INCLUDE_DIRS "") message(STATUS "OpenMP found but not the include directories.") endif() endif() endif() endif() endif() ## INSTALL directories set(PROJECT_INSTALL_BIN_DIR ${CMAKE_INSTALL_BINDIR}) message(STATUS "The install library default: ${CMAKE_INSTALL_LIBDIR}") get_deb_triplet_string(DEB_TRIPLET_STRING) if(NOT "${CMAKE_LIBRARY_ARCHITECTURE}" STREQUAL "${DEB_TRIPLET_STRING}") message(FATAL_ERROR "Odd situation where CMAKE_LIBRARY_ARCHITECTURE is not equal to DEB_TRIPLET_STRING") endif() if(DEB_TRIPLET_STRING) set(CMAKE_INSTALL_TRIPLET_LIBDIR ${CMAKE_INSTALL_LIBDIR}/${DEB_TRIPLET_STRING}) set(CMAKE_INSTALL_FULL_TRIPLET_LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR}/${DEB_TRIPLET_STRING}) endif() message(STATUS "The install library is configured: ${CMAKE_INSTALL_LIBDIR}") message(STATUS "The full install library is configured: ${CMAKE_INSTALL_FULL_LIBDIR}") message(STATUS "The install triplet library is configured: ${CMAKE_INSTALL_TRIPLET_LIBDIR}") message(STATUS "The full install triplet library is configured: ${CMAKE_INSTALL_FULL_TRIPLET_LIBDIR}") set(PROJECT_INSTALL_LIB_DIR ${CMAKE_INSTALL_LIBDIR}) # This one is /share set(PROJECT_INSTALL_DATA_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}) set(PROJECT_INSTALL_DOC_DIR ${PROJECT_INSTALL_DATA_DIR}/doc/${LOWCASE_PROJECT_NAME}) ## Platform-dependent compiler flags: include(CheckCXXCompilerFlag) if (WITH_FPIC) add_definitions(-fPIC) endif() libxpertmass-1.4.0/CMakeStuff/toolchains/unix-toolchain-local-dev-pappso.cmake000664 001750 001750 00000002524 15100504560 030702 0ustar00rusconirusconi000000 000000 message("BEGIN - UNIX toolchain with $HOME/devel local PAPPSO libraries") message("") # The development should use the locally built libs. set(PappsoMSpp_FOUND 1) set(PappsoMSpp_INCLUDE_DIRS "$ENV{HOME}/devel/pappsomspp/development/src") set(PappsoMSpp_LIBRARIES "$ENV{HOME}/devel/pappsomspp/build-area/unix/src/pappsomspp/core/libpappsomspp-core.so.0") if(NOT TARGET PappsoMSpp::Core) add_library(PappsoMSpp::Core UNKNOWN IMPORTED GLOBAL) set_target_properties( PappsoMSpp::Core PROPERTIES IMPORTED_LOCATION ${PappsoMSpp_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS} ) endif() set(PappsoMSppGui_FOUND 1) set(PappsoMSppGui_LIBRARIES "$ENV{HOME}/devel/pappsomspp/build-area/unix/src/pappsomspp/gui/libpappsomspp-gui.so.0" ) if(NOT TARGET PappsoMSpp::Gui) add_library(PappsoMSpp::Gui UNKNOWN IMPORTED GLOBAL) set_target_properties( PappsoMSpp::Gui PROPERTIES IMPORTED_LOCATION ${PappsoMSppGui_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS} ) endif() include_directories(${include_directories} ${PappsoMSpp_INCLUDE_DIRS}) message("") message("END - UNIX toolchain with $HOME/devel local PAPPSO libraries") libxpertmass-1.4.0/CMakeStuff/toolchains/unix-toolchain-local-inst-pappso.cmake000664 001750 001750 00000001376 15100504560 031105 0ustar00rusconirusconi000000 000000 message("BEGIN - UNIX toolchain with $HOME/.local PAPPSO libraries") message("") find_package( PappsoMSpp COMPONENTS Core Gui GLOBAL REQUIRED HINTS ${CMAKE_PREFIX_PATH} ) if(TARGET PappsoMSpp::Core) message(STATUS "PappsoMSpp::Core target available") get_target_property(core_includes PappsoMSpp::Core INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "PappsoMSpp::Core includes: ${core_includes}") endif() if(TARGET PappsoMSpp::Gui) message(STATUS "PappsoMSpp::Gui target available") get_target_property(core_includes PappsoMSpp::Gui INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "PappsoMSpp::Gui includes: ${core_includes}") endif() message("") message("END - UNIX toolchain with local PAPPSO libraries") libxpertmass-1.4.0/CMakeStuff/toolchains/win10-mingw64-toolchain.cmake000664 001750 001750 00000005676 15100504560 027015 0ustar00rusconirusconi000000 000000 message("WIN10-MINGW64 environment https://www.msys2.org/") message("Please run the configuration like this:") message("cmake -G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Debug ../development") # Comment out these two lines that make the build fail with #include_next math.h # file not found error. # set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "c:/msys64/ucrt64/include") # set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "c:/msys64/ucrt64/include") # We are building the lib, so need to export the symbols. add_definitions(-D EXPORT_LIB_SYMBOLS) set(IsoSpec++_FOUND 1) set(IsoSpec++_INCLUDE_DIRS "${HOME_DEVEL_DIR}/isospec/development") set(IsoSpec++_LIBRARIES "${HOME_DEVEL_DIR}/isospec/build-area/mingw64/IsoSpec++/libIsoSpec++.dll") if(NOT TARGET IsoSpec++::IsoSpec++) add_library(IsoSpec++::IsoSpec++ UNKNOWN IMPORTED) set_target_properties(IsoSpec++::IsoSpec++ PROPERTIES IMPORTED_LOCATION "${IsoSpec++_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${IsoSpec++_INCLUDE_DIRS}") endif() set(PappsoMSpp_FOUND 1) set(PappsoMSpp_INCLUDE_DIRS "${HOME_DEVEL_DIR}/pappsomspp/development/src") set(PappsoMSpp_LIBRARIES "${HOME_DEVEL_DIR}/pappsomspp/build-area/mingw64/src/libpappsomspp.dll") if(NOT TARGET PappsoMSpp::Core) add_library(PappsoMSpp::Core UNKNOWN IMPORTED GLOBAL) set_target_properties(PappsoMSpp::Core PROPERTIES IMPORTED_LOCATION ${PappsoMSpp_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS}) endif() set(PappsoMSppGui_FOUND 1) set(PappsoMSppGui_LIBRARIES "${HOME_DEVEL_DIR}/pappsomspp/build-area/mingw64/src/pappsomspp/widget/libpappsomspp-widget.dll") if(NOT TARGET PappsoMSpp::Gui) add_library(PappsoMSpp::Gui UNKNOWN IMPORTED GLOBAL) set_target_properties(PappsoMSpp::Gui PROPERTIES IMPORTED_LOCATION ${PappsoMSppGui_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${PappsoMSpp_INCLUDE_DIRS}) endif() include_directories(${include_directories} ${PappsoMSpp_INCLUDE_DIRS} ${PappsoMSpp_INCLUDE_DIRS}) #OPENMP message(STATUS "${BoldYellow}OpenMP support is compulsory.${ColourReset}") #message(STATUS "CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") #set(OpenMP_DIR ${CMAKE_MODULE_PATH}) # Unfortunately that does not work as it does not find the # include directories. So we need to do the work manually. find_package(OpenMP REQUIRED) set(OpenMP_FOUND 1) set(OpenMP_INCLUDE_DIRS "C:/msys64/ucrt64/lib/gcc/x86_64-w64-mingw32/13.2.0/include/omp.h") set(OpenMP_LIBRARIES "C:/msys64/ucrt64/bin/libgomp-1.dll") if(NOT TARGET OpenMP::OpenMP_CXX) add_library(OpenMP::OpenMP UNKNOWN IMPORTED) set_target_properties(OpenMP::OpenMP_CXX PROPERTIES IMPORTED_LOCATION "${OpenMP_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${OpenMP_INCLUDE_DIRS}" ) endif() message(STATUS "OpenMP libraries: ${OpenMP_LIBRARIES}") message(STATUS "OpenMP include dirs: ${OpenMP_INCLUDE_DIRS}") ## Platform-dependent compiler flags: include(CheckCXXCompilerFlag) # On Win10 all the code is relocatable. remove_definitions(-fPIC) libxpertmass-1.4.0/CMakeStuff/version.cmake.in000664 001750 001750 00000000023 15100504560 022514 0ustar00rusconirusconi000000 000000 @PROJECT_VERSION@ libxpertmass-1.4.0/LICENSE000664 001750 001750 00000104404 15100504560 016445 0ustar00rusconirusconi000000 000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. libmassgui Copyright (C) 2023 msxpertsuite This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) 2023 msxpertsuite This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . libxpertmass-1.4.0/Makefile000664 001750 001750 00000000154 15100504560 017075 0ustar00rusconirusconi000000 000000 all: cd ../../build-area/unix && make -j1 .PHONY: clean clean: cd ../../build-area/unix && make clean libxpertmass-1.4.0/README.md000664 001750 001750 00000002705 15100504560 016720 0ustar00rusconirusconi000000 000000 libXpertMass ============ *libXpertMass* is a C++ shared library that is referenced from other projects. The *libXpertMass* shared library is designed to enshrine the non-GUI functionalities needed by the following two projects: * MsXpertSuite/massXpert2; * MsXpertSuite/mineXpert2. In particular, it contains abstractions for all the chemical entities required to fully characterize a polymer chemistry definition, as shown below: * Isotope * IsotopicData * Formula * Monomer * Oligomer * Polymer * Modif * CrossLinker * CrossLink * Aqueous chemical reactions (cleavages, enzymatic and non-enzymatic) * Gas-phase chemical reactions (fragmentations, with a sophisticated grammar to describe complex fragmentation patterns) * Isotopic cluster modelling/calculations for any chemical entity representable by an element composition formula and a charge. * ... *libXpertMass* has a companion GUI library, *libXpertMassGui* , that is designed to enshrine the GUI functionalities needed by the same two projects above. libXpertMassGui =============== *libXpertMassGui* is a C++ shared library that is referenced from other projects. The *libXpertMassGui* shared library is designed to enshrine the GUI functionalities needed by the following two projects: * MsXpertSuite/massXpert2; * MsXpertSuite/mineXpert2. *libXpertMassGui* has a companion GUI library, *libXpertMass* , that is designed to enshrine the non-GUI functionalities needed by the same two projects above. libxpertmass-1.4.0/doc/000775 001750 001750 00000000000 15100507137 016205 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/CMakeLists.txt000664 001750 001750 00000001561 15100504560 020745 0ustar00rusconirusconi000000 000000 message("") message(STATUS "${BoldGreen}Starting documentation build configuration for ${CMAKE_PROJECT_NAME} ${ColourReset}") message("") # ############################################################################## # ############################################################################## # BUILD THE DEVELOPER DOCUMENTATION option(BUILD_DOC Off) if(BUILD_DOC) message("With doc building (-DBUILD_DOC=On).") add_subdirectory(devdoc) else() message("Without doc building (-DBUILD_DOC=Off).") endif() # ############################################################################## # ############################################################################## # EXTRACT THE JAVASCRIPT REFERENCE add_subdirectory(javascript) message("") message(STATUS "${BoldGreen}Finished configuring the ${CMAKE_PROJECT_NAME} documentation.${ColourReset}") message("") libxpertmass-1.4.0/doc/devdoc/000775 001750 001750 00000000000 15100507137 017451 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/CMakeLists.txt000664 001750 001750 00000003047 15100504560 022212 0ustar00rusconirusconi000000 000000 message("") message(STATUS "${BoldGreen}Starting developer documentation build configuration for \ ${CMAKE_PROJECT_NAME} ${ColourReset}") message("") # The output of QDoc will be in ${CMAKE_BINARY_DIR}/html. # We need to copy the images directory to the build directory. add_custom_target(doc COMMAND /usr/lib/qt6/bin/qdoc developer-documentation.qdocconf --outputdir ${CMAKE_CURRENT_BINARY_DIR}/html COMMAND cp -rpf ${CMAKE_CURRENT_LIST_DIR}/images ${CMAKE_CURRENT_BINARY_DIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} COMMENT "Build of the QDoc-based developer documentation in ${CMAKE_CURRENT_BINARY_DIR}/html") # The output of QDoc will be in ${CMAKE_BINARY_DIR}/html. # We need to copy the images directory to the build directory. add_custom_target(debugdoc COMMAND /usr/lib/qt6/bin/qdoc --debug developer-documentation.qdocconf --outputdir ${CMAKE_CURRENT_BINARY_DIR}/html COMMAND cp -rpf ${CMAKE_CURRENT_LIST_DIR}/images ${CMAKE_CURRENT_BINARY_DIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} COMMENT "Build of the QDoc-based developer documentation in ${CMAKE_CURRENT_BINARY_DIR}/html") message("The devdoc images and html directories will be installed in ${CMAKE_INSTALL_DOCDIR}") install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images DESTINATION ${CMAKE_INSTALL_DOCDIR}) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR}) add_custom_target(devdoc DEPENDS doc) message("") message(STATUS "${BoldGreen}Done configuring the ${CMAKE_PROJECT_NAME} \ developer documentation.${ColourReset}") message("") libxpertmass-1.4.0/doc/devdoc/developer-documentation.h000664 001750 001750 00000000073 15100504560 024453 0ustar00rusconirusconi000000 000000 #include "libXpertMassCore.h" #include "libXpertMassGui.h" libxpertmass-1.4.0/doc/devdoc/developer-documentation.qdocconf000664 001750 001750 00000010637 15100504560 026027 0ustar00rusconirusconi000000 000000 # QDoc is a tool that constantly evolves and there may be compatibility issues # between old and new practices. For that reason, QDoc configuration files in # the Qt Project includes compat.qdocconf: include(/usr/share/qt6/doc/global/compat.qdocconf) # Give the documentation project a title: project = MsXpertSuite Shared Libraries moduleheader = developer-documentation.h # Pass additional include paths to QDoc when parsing C++ code for documentation # comments. # includepaths += -I/some/path # Paths are relative to the directory where qdoc is called, that is: # /home/rusconi/devel/rewrite-xpertmass/development/doc includepaths += -I./ \ -I../../../../pappsomspp/development/src/ \ -I../../source/XpertMassCore/includes \ -I../../source/XpertMassGui/includes \ -I/usr/lib/llvm-19/lib/clang/19/include \ -I/usr/lib/gcc/x86_64-linux-gnu/14/include \ -I/usr/include/c++/14 \ -I/usr/include /usr/include/linux \ -I/usr/include/x86_64-linux-gnu/qt6/ \ -I/usr/include/x86_64-linux-gnu/qt6/QtCore \ -I/usr/include/x86_64-linux-gnu/qt6/QtGui \ -I/usr/include/x86_64-linux-gnu/qt6/QtNetwork \ -I/usr/include/x86_64-linux-gnu/qt6/QtSvg \ -I/usr/include/x86_64-linux-gnu/qt6/QtSql \ -I/usr/include/x86_64-linux-gnu/qt6/QtXml \ -I/usr/include/x86_64-linux-gnu/qt6/QtQml # QDoc needs a lists of file extensions to know which files to process in # different situations. Uncomment the following include statement to get # a pre-defined list of file extensions. #include(fileextensions.qdocconf) # You can also specify file extensions manually. headers.fileextensions = "*.h *.hpp" sources.fileextensions = "*.cpp *.qml *.qdoc" # The outputdir variable specifies the directory where QDoc places the # generated documentation. outputdir = public # What format to use for generating documentation outputformats = HTML # The headerdirs variable specifies the directories that contain the header # files associated with the .cpp source files used in the documentation. headerdirs = ../../source/XpertMassCore/includes/MsXpS \ ../../source/XpertMassCore/includes/MsXpS/libXpertMassCore \ ../../source/XpertMassGui/includes/MsXpS/libXpertMassGui # The sourcedirs variable specifies the directories that contain the .cpp or # .qdoc files used in the documentation. sourcedirs = ./ \ ../../source/XpertMassCore/ \ ../../source/XpertMassGui/ depends = qtdoc \ qtcore \ qtgui \ qtqml # apt install qt6-documentation-tools (for /usr/lib/qt6/bin/qdoc) # apt install qt6-base-doc-html for the qt.index files QT_INSTALL_DOCS = /usr/share/qt6/doc/ indexes = \ $QT_INSTALL_DOCS/qtcore/qtcore.index \ $QT_INSTALL_DOCS/qtgui/qtgui.index \ $QT_INSTALL_DOCS/qtwidgets/qtwidgets.index \ $QT_INSTALL_DOCS/qtxml/qtxml.index \ $QT_INSTALL_DOCS/qtsvg/qtsvg.index \ $QT_INSTALL_DOCS/qtsql/qtsql.index \ $QT_INSTALL_DOCS/qtnetwork/qtnetwork.index # The exampledirs variable specifies the directories that contain the source # code of the example files. # exampledirs = ./examples # The imagedirs variable specifies the directories that contain images used in # the documentation. imagedirs = ./images # Path to the CSS files if you would like to use HTML.stylesheets = /usr/share/qt6/doc/qtgui/style/offline.css # The string that actually includes the CSS into documentation file HTML.headerstyles = "\n" # What to append to every page after header HTML.postheader = "" \ "" \ "" \ "" \ "
" \ "" \ "" \ "" \ "libXpertMassCore and libXpertMassGui Developer Documentation" \ "
" \ "
" # What to append to every page after the content HTML.footer = "

Filippo Rusconi

" # Set a warning limit. QDoc will exit with a non-zero exit code if it generates # documentation warnings during the documentation build. Useful for tracking # down documentation issues. #warninglimit = 0 #warninglimit.enabled = true libxpertmass-1.4.0/doc/devdoc/groups/000775 001750 001750 00000000000 15100507137 020770 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/000775 001750 001750 00000000000 15100507137 024216 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/globals.qdoc000664 001750 001750 00000000206 15100504560 026504 0ustar00rusconirusconi000000 000000 /*! \group Globals \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title Global variables, enums and functions */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/pol-chem-def-aqueous-chemical-reactions.qdoc000664 001750 001750 00000000400 15100504560 034525 0ustar00rusconirusconi000000 000000 /*! \group PolChemDefAqueousChemicalReactions \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title Aqueous chemical reactions These are the classes that are required to model the chemical reactions that occur in the aqueous phase. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/pol-chem-def-building-blocks.qdoc000664 001750 001750 00000000427 15100504560 032374 0ustar00rusconirusconi000000 000000 /*! \group PolChemDefBuildingdBlocks \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title Polymer chemistry building blocks These are the classes that are required to make new polymer chemistry definitions that model the chemical structure of a polymer. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/pol-chem-def-gas-phase-chemical-reactions.qdoc000664 001750 001750 00000000377 15100504560 034730 0ustar00rusconirusconi000000 000000 /*! \group PolChemDefGasPhaseChemicalReactions \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title Gas-phase chemical reactions These are the classes that are required to model the chemical reactions that occur in the gas phase. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/pol-chem-def.qdoc000664 001750 001750 00000000536 15100504560 027327 0ustar00rusconirusconi000000 000000 /*! \group PolChemDef \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title Polymer chemistry definition These are the classes that enshrine the notion of \e{polymer chemistry definition} in the form of a complete set of chemical, functional and computational rules characterizing the chemical behaviour of chemical analytes. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/the-prop-system.qdoc000664 001750 001750 00000002163 15100504560 030145 0ustar00rusconirusconi000000 000000 /*! \group ThePropSystem \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title The property system The property system developed in the context of the libXpertMassCore library is aimed at easing the aggregation of typed data (integers, doubles, \l{MsXpS::libXpertMassCore::Modif}s, \l{MsXpS::libXpertMassCore::ChemicalGroup}s...) to objects that derive from the \l MsXpS::libXpertMassCore::PropListHolder class. The abstract base class is the \l MsXpS::libXpertMassCore::Prop class. The member data are the \l MsXpS::libXpertMassCore::Prop::m_name string and the \l MsXpS::libXpertMassCore::Prop::mpa_data pointer to void. From that class, type-specific classes are derived where the member data are of that specific type. For example, the data in the MsXpS::libXpertMassCore::ModifProp class are of type \l MsXpS::libXpertMassCore::Modif. The data in the Prop-derived instance are automatically deleted by the deleteData() function. Only the \l{MsXpS::libXpertMassCore::NoDeletePointerProp} Prop-derived class has as member data a pointer to data that are not deleted when the property instance is destructed. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/xpertmasscore-mass-calculations.qdoc000664 001750 001750 00000000432 15100504560 033401 0ustar00rusconirusconi000000 000000 /*! \group XpertMassCoreMassCalculations \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title libXpertMassCore Mass calculations These are the classes that are required to actually calculate masses starting from formulas and a polymer chemistry definition. */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassCore/xpertmasscore-utilities.qdoc000664 001750 001750 00000000212 15100504560 031766 0ustar00rusconirusconi000000 000000 /*! \group XpertMassCoreUtilities \ingroup MsXpS::libXpertMassCore \inmodule MsXpS::libXpertMassCore \title libXpertMassCore Utilities */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassGui/000775 001750 001750 00000000000 15100507137 024052 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassGui/color-selector.qdoc000664 001750 001750 00000000123 15100504560 027647 0ustar00rusconirusconi000000 000000 /*! \group ColorSelector \inmodule MsXpS::libXpertMassGui \title Color selector */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassGui/xpertmassgui-mass-calculations.qdoc000664 001750 001750 00000000165 15100504560 033074 0ustar00rusconirusconi000000 000000 /*! \group XpertMassGuiMassCalculations \inmodule MsXpS::libXpertMassGui \title libXpertMassGui Mass calculations */ libxpertmass-1.4.0/doc/devdoc/groups/libXpertMassGui/xpertmassgui-utilities.qdoc000664 001750 001750 00000000146 15100504560 031464 0ustar00rusconirusconi000000 000000 /*! \group XpertMassGuiUtilities \inmodule MsXpS::libXpertMassGui \title libXpertMassGui Utilities */ libxpertmass-1.4.0/doc/devdoc/images/000775 001750 001750 00000000000 15100507137 020716 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/images/flying-frog.jpg000664 001750 001750 00000107451 15100504560 023650 0ustar00rusconirusconi000000 000000 JFIFddExifMM*C  !"$"$ $,"B!1"AQ2aq#BRb$3r%4CS?&ı=BI?a'uskq~\%| dmR9uRn5Uu,9'>hKuèhdr|N*22H%YH 5IN̓mqêlL*"1Drx_O?/xWrUGJ 'WeoV *nsΫ49s VshcK|dA$lz?(Ԥ~#Λ$ rc$qA=q7jJ[v$2emyOQ|k]AC%rN\V#6ާVVR K6Ye2!P:5E+OK̒d]%]؏CGI$/rN/RQ>F 5P1F+& 03qߍCAt4Ui`.?8}'<}D /q܌ho[ZTHAl@'lk?jㆢx)OWs!o1*<=zXvIՕ=#!*qa{$ҽJ=^*"0LPG$lA_|jΤʹ570l0 0}TEY#mAGV00q?aO$o J1 >G-yI@vJƓW#xPxj[ <@=O0NL5rq|`R5#K1ѓ9%+)Õ`ò$Lp F}2u ;dq)`7 z' v?~uxǏ^}5 zWrGa kg -]C$;TKbRBd c' sYS|ODxJ22Ӿr1i3>! SثȬ0وC8r䖢ii $q8Gf!e>S^r?ֻb#fZg=UBgr̅_/$c?gYlj׀[(AO'Vؤz=U?̒LLc9ϩVjh(++cS:FN0924&IUUi ehh$e;F9'CC:rbJ*;3\i<९UM?$Ay0 'KhK%ؠk}bY&i*)@* (W:kr^MQ Y1d])#T +Ite;{=/PZL!PO.y .Iш]qpV̌QA]2Qwjodh Ȟ+I>RcFpr1Ƶ>{4rBfv 0eW n+1pNee?I:hUz̐r?}r"fy{GNhiu(+3D=,uUsDΥi)e4 c<w:!CSW30MFX:HA$;a;w$څv07m}c=(!-%DRE0:&:@Fs >+#BYd@+gOM~bQ RM@V0HSqOtq4Yiϖ#*e uNo-u7ZMi]:-T ?P8##=T$QGZV$doVpx;܌UJLI,hFYr70<3#g+k 8#.EKO* v\`cmI%I^n_XХ2N(8C +V-`$E̥=9֥0R_4QM[YQ% oG i.U5& I&]u2H3{ Yάm䢴x$bf;V#[`w+rKq,:$(+>R?qR5N/54" 2*̠~N=޷G,N@7,ۉ${^~eN<5ZYo O:Y'ҽ3RJȣس2Un9tV+QA-\XiQ*T .q]]S-L9E%X_(Co#?S,yMУ+ a, rIڣJm5ʝF~^ac *yArI֓еU O-Ypj`]6dIY P6ksk\Q b[,%]PsA:7 g:Aq R'9s!T-iApuRdRҽ>U9t"teQ{p "B@ î I {U꿆j;qDv˱# >_b;rۅ-2O[S |\>7qYbK 20չ  $ǮsG\NS7K(C@60H;xu/CSvc\䂧̄d@whmQPKt)$Re$-8ƞbߗP:R3gG9Y)CS3#ӂFS έ[VfHa嗐T7m<|/X'սT^#(@Zyj͌0VX+UuۢVII]v;AԒp [06k`BtEO@=y婂:JWd7 ؇'${M_QYb)\OT>d m.WS\kjZO e#gF8'Г@:]IJH`a$T7o|yQr^R iK>`>i C$2KJ ._&r#>;,1M";Fd32I[v{!®%\c$Ԥmk*v 6I։Rߺ9i(婄G:x-=s- X3Xʜ Fv(@3ke]Ze* e@SNO 'Y Ңk|I2oUprcnpTy cauj'fC {F6ǠAḚTQCB<*w x-. {q4ϒ]8$2? Tw l (p*Ѹ)29W EdTTJjtuMʜh r{MSCQ5O#ű~YhQP2OR[eƎJg*ѿypNH?Oc+9$ aԳ UbˆCH8 5*R+ͶD\v9Xz, %DP Gv푪92P\( R9q'M\i †;@͞FA;jz˛[*H噌m#x&Fv9$FOs.Zי4 uTg|5&l򺯗o 5S3OU R;sǔg^|=DdJLMdѱ c:窒c*BӜϷR'ie@`[$ 8[XZddo?V8^N9gTT]oav3y;"VGqdD*aTP/=NN("{\"iDd6w'@RHhj0N>sOnuҮsJƒ7Hb8껟QS5:Q$0I̞cJ-r3o'G_.<;gAؑ^ꤢIZơ@ʃ)lzTNʩEI)MS]PI=q=3Ó=i_jfUC=D1s³FnxPuܠކmE9vGhAPYXq7W]cgX#bR I8B}8:bՉR}uiFe$vlypAAǦ@^) MNG$NA?:ȥw>G e|Fb97=Uu u79(ac)JrTF. T4UKOzm{|+ȕ48Xn| gVt(P)1uBYnw`v+^-WSIB;D{ PuW룎=י'ąddT c^s㾝vm=5b7#g GimtUV !b98 @~t ۣnE=E^$;(U GRG*4RIe#2@|z滧 I_YB%r Hڒ9:IKEYr7vCX&ꮤh)Q5r$1 O:sΦZm=',6uLX+ xB

it˄ТՙDV98Ǘӏ\U\ )Y]$*8/sɱWnX3F欬7@#89R[-Q 9YaBX189t[:M]+4L_`9%S䏷mZ$h!Erw:- -׸(;$P-eMF܄_#~rILQEY-JLt1̭MCw1:SuUIJ`jpM*p! |ǃ# WYq450[ikxV' #oeS /PGC!/pW0*4qǽ X2:/K2ΙY_NeӻW$`g[JiKQ[,  8νg$CXXgr䓞4>: \BRYFX9`FI r+pתz`^Bܪ@ _ ոQ%H=;l)Na3 'f k6AMHTe|A Ϙ1ʜce8p+EE4 wG'$iLYZzz*X%/f!'sy&@TSռ[eH9U0N8ǔ`]+qIFQ$<`Ild`uڲyx%i6RP9NGw$5hiYoEYeg׶Ӟ.~\m-d\JdI$U&ܹ^q!w g] -H:jʒnnr0x \^&pYXĈаW ;v}OWn!wJdc锣&{;Cp8<v: m%‡Er㣕_\p Ǘ ƫcd9w|9`W$H#BiG,7Z"Y $1\;:'6x{Դd+&Qݱ8{iD P7𥉉TCG, ,*3 >5Y^"Ibb,QX+$ ΢kTIZ囉"eb2bsέ]-ȢUw:sO$Uk:fju%sYL)qP0N7v?`*EZ搴Lš2Hϯ8:N32R}0Lz?8$YajKZN]Uik))\en9m; mZ]lvxaf@X%H}0GU PCFQ3ISö| #r m5CKpTTȊc(<9pϥ*1- 'U e>v#ђׇ`1VvX:U%r1=A^祥_!\1`/4$V+])m>`ʅS_tyfHŒUAJ\S‚J% /FŽKrF==SkjJFB>ed$I^Kg9ܵxv@( i[kd>]zLR'ebfSǶ}*ykkNhs鳔Z&#P)lIa? ,ԚSU'H<vIiW-W2C!El*1mw$N 8ݪ};j\u=59A$$d#c82 z0O Z\F$D| 89Yjk)M,s'IP.O#kW:lA"#2~85sb*iṘiq]qL0Vnڍ)Z9KF$o|UP$5He71l9ېdtVOCSd*ᚏnQ8dqcAӢ:ZyRU9|31P!8 x9q`WKRs!UM?N s gpIdt()嬊g^",7vcWbaxu &ƄIjs?aԔ՗{LVS8vUWJȐ H~9.GSQS!" eF@gcc螸kTm\Di FrۻEe4TXjl\+V2K2;cPXhnUtG$5URi$=OGn}gv^&jVVQSxP2m]zZYVTo5Ӳd8 ~܏QΫOSS=†Rc` ˕H'OP+㢞X$ S-P Dd(aO}qm?tqQCYIjM/hؼN09$HzKkJL*HڌHռ0NϲQKHaCm0G9OMb,drW(rA~O+AS=7OQcfbd+v\)'F#O\֫2Uq;naAn~wTSMA:%& I/Ӑ@ʞrj'5U,®|3( bs$ TYsS1V pr €s 8x&ìS$uX7-;rc8{ډ)-rӬ-9My ry :/j_R+K$a&p$eG-`2N֛ T*N YێlxqFn4UYjå_"U0%O N X*d HNrphۍ1+.&ESKnm9{= #Vv3#V= >g$1QR# cН8)8m}Z˅5*f EBWdCT W)y=Yj)A'* px`Qu=nSđ@&USQ$Gq5/A1{'K(&%Wcq9 8aO@a$U@UmF0?lcیq=UpxO]] #gUJwwD=1:)p }x|Pj&jA~y#f2 :N*R|@o6U/I5.v*qcۏmhk_W WQV|ݽH5St+PEOqS[ $fi`[wG3tm8kS)MK!GpQ#gA[zXE](/$(ԌwpΘZzVI%k),G)`qU$ ;Օ tCRwڹ*`3BYNH,XlN1A grh5k$"jI9#psF4A- 㟾@'}4jaa#*"f ArH>mL#6Rq$1yǦqgC(%sB(88n;~YdǵWUHB:A,09YT;/' 1 u-lzU.# sp΢xEvS2}J%VHXM0Ǐֵ 5wDP*TI#ߞ/GӰ_[/OZxRFXsr3[eXdrDlRs:ij0WWWXȒ1L: 'FahSF!iv3qh\ݣQ ǿM cLoA;i-tS8H<QV\m<A$Lf+yeUUW5@TD YK_Etd%E<򡑎H q8ELOruG+#M,4P?yruJ.y7Y6V]aO墊(*I!T`P;wƎ|<]:jt)aIru(J)$'ĮJx]jǍ eqߜ0Y~xoV%JNUUX*iG y$5YlU,R^Y<\1px֑җ:[U‰Y#@Hf`rl==Θz^+Li1q7$wf=|aGoCS4Ē`={c\gfiP,XAzgs[xgF%wFTPM|ŞJDZU OùCKWIE1éhr*  W)2NKJR*UA߱u-R{n{ g\](%p*2>MāF'> Sw_ShC`RNܒW0s8:+5䜃CSL3H<g[x6JZEYȬr~'34J h$j,9qt/mUM|#/q {zѴ[`S"IyŁ#q]_=/o O%$FL۰H8lqG3U%-=uG7: OT&RYd4m%HpG8 ?':_}0z+gSN\ 7sFL.mud:QXI?m.GDZEG146oO!) GrưVe ɢm8z߶ KCQ]Y^pH!sT1هs\:M,M# vqԕ eU Lr@^1uҊqEHz+7LZIO'%$N9>n@:mZb C6K09 ,ǑmK8d2a\/YW55mZ ?+n89#X5=M 枮d`Y=cOtWKe b3W*'Cw45A%$v8r?gg+E+L"8YSII.qM  r`~7Pݬ:OJ9$r$Rǘ@0F׷KSUZJr;er;;gp維WuwR\)Z;:h(VܻjcN;ui:ϰ5]5_dH~=qLJSZ TT ӴcpU qsv-t7RI}J9ת*먦H\Px&5 ږd(r@Y%%o‹LFZRJ bAPà 1Jbv0Ǯ=<}<{阻-ߏ 8{+L[[ѢZVRͼϦ탎4b3IdtuO?H9$FO? S(؝mtxJF><}4SjR@4vr>ӊk,&6&z[q8z(/;UAjXg# ǘyGE cPIfH 0I#wySl yp;O^庂h+SLE13van$qU} Y8w}6l56+(鞦3Un9=|{#\z"oH 0,n e 3ܑK$*>ndfDXw98qt'"н%#SʡYXwÂ+tQښҁdbr8#A:BZ^ c80qOM\# Y$lʇ,)QUG]U<C%"#py~{MHZwBH'?Q7~tWW[('le n 5u»LE]E/X G#)%:bJ:eiKQ㔅G``1s[b *ҼoM[p=F.O/Or7l9nX0 ~05bf@/ZVmϸ!G}0tr5ְ Nx.w vF\8EJZbA+KNd`ۃp0!A#4UbFB\}8d]G? P +%su1!pH0A WAiRKW2~Cv~qq~{lYn#45YTD󣁴=3+!3x@/JcwG遮::fxT|\ vt;k*:TQRݎ1ƙ2m Pt$I O;{8|uGbr=:I1]ބ`q{ҕ}bAoqSTȃpO>\gZ} zK)V%flyvS\/5xǶa\`:DD0T\)R@8J'I,烀EmԴBƔ;E{d6M< pUUU"VRq%}y={ Q^ %#GGlz~XMVk dk:Γ@Fe!ru|BЧĈ!⚢8*Q*5.<p}}ޞnT >2J22yz;oL^]O70t@&Ф`#d >\wiIʉT#o{S?^#dCpvvfZAI݁߱4{]Smv)n8xz:1"ʫFY99pN5:"rwlmRWu=Lrgvce,T4-KRą.過r@8>j{GGu G,v'_Iњ,Y@NqqYϨM4 дw8||h/F MMQ R4{vcN8aAU[-w*C)$O9'| '-zj' q{BVt\b F@88Ch\hk7a p9lU\H vR 2QppN squ7Qi#2(U*97MO;ѫLDO+ op39ƺMN)>r+$;%-0PQǡ?YGluFF"O]-IhT,52<_C43k*[ŢJ <&n!~O9 v:i^@ȱ298'oN|?|][8 ߌSu,`(n Z3,09mNёns4ω5 . ɽT U3`Is}3ti *?\g'!#V/^% H)E zIHﵛiHurM= (BB8,88$ijkd%r,v܂۸8T:|lRTxPI qOqQӽx tQoXǯ֏>:mh˩|bHzHLY[]?/ie5vp+Sg7R06?'`݀o7-WjD<:xx“;[+:h/s0V~0ܓ5Ӫ,ݚoZ^)6(=DZGzj)cyYSpW>΢D"ŽK82HN~5Y;Ouڂq%Xmv\Iq4Fʨ*_ۏoN /l/XеҮ[!9#r22{@Z gn7'$}ΆX>{BS[%T|6>0w؞ĩIdwHp 2p 8myj|ZtcL! eO/K%YFc As1UZd\SԵʲ h`Mrw'<7MUuA^?QS-\8#. p4wyz[m{!EzV0nf“a'YeXka]BA'=v OYq48&+b8h4tUR[dH*X+gx[ۑ7VKLԴ2xy{{>K4%71 8?ޙ4? Ҫzb#!ܞg@=mZ꟪4I)hj|g)? žΖU4#GH|l<)瑒}\Ru1*JH\Ű{S#Kaz*ڦ4 V902'61%W&G1rU!Gn2sOz~C5cpI{zUWQDL>}8:-tV!ڋ&x܅"bX^> RvSE]+U!M\ eGv 1(V&$)Ò YwX 8lKqZ֍PGQr8BQ#c:ey6[mIշ`7rN+!#qL.|A-,{N4ӹ'0t[NfSEi$O >5uq 5(qldیc?{ v"dU9 9mznu:  Hy@wx {k6>PKQ4G]W$crđmݶsA=[d~f1J0`{MªiHxQ,3^m-#` (;8úIʔӬ1UtX Sh+ "(ʶQCgذ麎AOc$?bQ9f{ YvJYQ:o &VwbG FO8>¸hH(PPrʿ[}sžMODbIbU')a%RG&G>19jZJUAH9`?Hk(4eE|#n;]_c z4E]W,IOpNT?SEO?P4SZu4{a.I;HR}EEsImfvU)ܩFS ps9":>/s+hĝ3d nw :KAD:JՉ*!n\Fy؃Vi#4D@n=Fяh};'0C3Bx;b':u+^%V81伌:v9FWh_t6f1_K}%fی;%<1DNU]mUʂIOWૻVoDJ9RIcJR 23H])ac+b{GiԿWVxhcH[pBXD&:ۃp&y;ԯ;wq1;BzrK)QwΑZJ$X=V+mzU!& dr/m#\t3ENM4#K;N90;q^vHV2*@Q.9{ji-}EP'ST7)}{zˤ/ݺU0߂N8#ۃCErJu.Hy9}Nt? ~Aֹ˟Yd}[ږ+OCt4t[bu;| q6\>DӳK6I>Q:tж2/' ʾwzs ݅?LZ*>Lf(PγhVi"JݥI!@TUn%mUܬ<ʋX_F  =sQ[mԐCWTQO^*"jtFUٛ !QxlWuUO"U&W y+Fxljm5UC'S"~T} *8:_„=KC"MW5+3"ЧlϮ# 8Y/ƚ:J[[S%;#88Wv,8Ԍ1-9,Nm {h̑HJGYTFOoƕ:V.x <=ĀpsYQdRN? (]Km (gLK]TS-4=;F7%؞Ar6ޣtv:XjǑ>^zF-$13#.233ƧuLJ!*qXR1~qBT ڮ2Ξ2I'ۿRd_1P1BvqsdZk:Ji#˸eY}{*Tu4]1C>j[G!e=X.yg+]m :tA"9K [yǡރYiU v2тFq]g%a/,U7H =rZ斦POMnjV!jH驮4 lK,c9' 8׶mEWei VO 7#7Ic:hDTiE[fls#oh(BX"R7]rutǕlxPi5 qs=Ƅ_?%=m¡5$XIǮ[` CeiuERqс <9fsm Lxl,y@_4  XR02qIyJn-$,άQbW"^`7ޠU_ǹ$&# `>^05$-fL7;}ҮZڥD;2a\! Ā{ë_LQov(wjkj*a6`ĨIt˛d?)ѵ8n2HΉ9>O%o^j HiȪO2[9]-T74v vva YRj  ʕڻ!s$;Vl`~o\ddk PFԒL8 n2G2q Jrz6sZ<$G%} 3κRCIMR~l_0@R9''\4#UnP!!P{r l:dꊊY&35@ 8ףz0[ld,Qym$6ߚ8gTCoUv  9U-:Gqd|0<㓑2sM7{ek=dT1x(I9d{gxڝjI`Jx{v`1^9ƨxng暸Č;G*yB8'jҙ*+*HvQk?;uָA--ԥ:GHbROMޓj-AM"*54L+'@ف4O֕R|Q_, )$󃹰ǒQ\i,:0 ,Ckli GbV^.bĞb Ȳg1NBQ+Y~+z+j"jYUdj2r ސ{kMG?B&07l;kE-Ss2{M]RcO$jH%P'= jLRdvc12;jtO1`BN?===G_]QS_MS=L[z}d`c+f({\nsgCI@RrǩIT*+~~j4q#2{}JQCJnwCO RTfa9hm$*[`-[9~ڳ)Y p8i~Z <4Wjqk!램ZM6|/,=}5UUz @|8suBUq*%HTI$fRH˸"d=?n5n%A Ed~NɑryOa>hje1U#!C< {?r~9z$BƞdiU9_0 `=RU%)LWa&Դӈ'@e d;2Q:O,ׂT򃴂 e8`h]-K4Tӵ4JUHa*Pۏ AzgɼVWȣFĊb0#{ی o]EԴQQ2 *r8/l+igMJCL7܁GwV-$F8Տfb.Q*I@ɥYKYM=3 f])g}<)s8=SVt9$`MؑQ`@Nhk}<($*Ǜ[YEJ6j\G a9‚8/ (.^iXTȻ gqf%?O Ղb78LK(?8aߎ:~*Ji.P\@HD?H& *>bhBt5EZu;@!o8@A My-:FRz)ħx*Υ8Gm$4*dl{ ڪh/R$g>qKGweM`;SJ*İ#npb1#ŽIԆZB<$J*yz-UrfI!jՎBgV$HI$18_lƪ0;GP)9ӑ1;ntpW̵g2@eQkc ƄB\)1G4pwDG.$dA*?#Wm=:W91lJ#jI-SRS4Egu_H3,mݛۃqNudEGl O8`SО]qjiE2^2; KWDAj"2'D*d@AN7ysqG5<.2*HWK,wl !ۥx9MFeh8pO=:ήVJC=}tIC$aM݀}`Lh[}e4WV8S[g367g )}4AlWWzX#p6֫ru2Ix vV]w鎟ZqWIr9C驚黵5Ʒ4ȊX(J9@βmKT=!))Hn gifΓR\`o&InwO m {5d7RT^Pq#t}u,t(PqDž1n=>Z&v 4={K+UD`ȥG-{$8#Pc6+31qF;:~ڪmZt*W$x|͕PUmMTw*g`-Ix8:+njX̅Qјʎw OzLvTnD8)郇9jz 6ͭ(TmO~}1I6#0,p=#h_QS?Oc\qp?$~~ihꨨ,)Ձ,MI b" 1HlkGIswfYcF8\cƪAФjk R|>k"4ԡ#ҿBr8'{}訠*aꭆ#' ׮c=Dptp6Gd#KޱI)$Um3$ HOnAIi[lU6䩹FSI9R=kU*|ʊ5lFh9#ՇoWd1ykk$G4nYK)qqkk\uRvZE< *y Gm5[ME=&YV < sK`;zgݒgH$[ZfEP2(c= v@۶8$OTPGQl@FC`c׸ҖIKKWyI7UM6Fq$8:\u{L]7K/"^RFW?N}x_V-U{үIEHOr=F~! }5MvnO:m|QQ?NZxA8WheH$(R@xO MyHe&;;(Y@ }iKMHVE[v$DߠX/tO$*{d4o߾^~ ZmQ `Q} ҝStqӥ‹yZ=v?~u/Z Q|P[nΫܦk#ıM4q![G-}i䤏Q)!{`[$񭆦Ocǐvۖ.%FRᇆvu֚TG:(>~1롕)IpѶ<BcVJ$tcǑp9'V)lSOs-*X\qb^y$yZM }HE)hy`#4{N  tVvXcȿH&C8$z)o @hť'hnb('pI<>'L1YތJ`3; Qei%v>})?N5c5:vb&tZU9кYRcIg u7+mtr̹V x8aL|6hZI,]AW䤯sQDcHGWkPiV1ZAP[k _ÍYV)@N2Wq"fGãTp-;;䌁OG D^$1Qdw: Cн9GKW F9=Xm'EXJ*(].I$nO·IiK|#*9;-&y8!YAQZB3ߟJ<7Uẗ '_?lhkV7J u"%i}1)E9+ap?Զω:ojxc˰c`x>>nGANG4ew>V>EsJXPh:hjɪoVϗPLJ}҄1!'$ c[ʾqܦFsMq[%cq q'ڝdF#u®yY+Jc-=%@59|}*1edX॓枪AϧsH&6 v%XVWZXt"2F;Ft%-Tz+=[x@\o_mCUv ޖ"R}3SRSHGjJPI-9԰uߨnl&2 pg;F>i:[}\K4~^R{1õQhW0L Bp d)8]&=t O造0Sz֎TѠcGR8ήtuR,_rx鑤ZY.ʠp#VE%GRSR_j5I1@*LUrA OWɕ@j]k}H ]$nF9hGuZWHhd woeO)z-T/8 {<p1`DNWD $θ늬$AVf.DMxHq΄WnV\*\>ڄq,}rE#A&Xnolzj:0hey鲕jkIՕ&T8*}Gn!a$zX'hG$Ϯ=L-D4Ƞsx;?wDY,Ƥ2yM֋Qz H|(0 gxtm:vBI`'y9[?Idn'-w k re:[q?nuG'agSi`5$;ƺ"{QDSI)%itAWY]$9 d'a.Go}2P|JԫG=:դOTѽ-LR(7.3"/7]P'.Qh)oFqk8[F] &O8=mK2OAov%??w|Z֎*',pmuKAT 19VЍ*eQ]PÌ!X)UlMe]2 -ļRÃ:ծ6XzcJxXĒ$fDy\g$ L+*Ju b$FܒvyƏ߫)mveVoDx,U p?̺ 4S<`v~,Z&9d>hORP]<pن9A=~'%kVv;ch LITdi܍= =sHwGe"Xw184%I~6\sr:="-ev I#`cۑ\v](0xӿmEGzb(nD,˲gO­F RD>]j*KUm\X盓#-}qmI,}K@Tḥ(VیNO?sR+D$<'=9@Ni*EX79h:FU*Å 9,X=MQ$MIAnq#b}~uDT('s%@Tr4bZ lMÿ'[AQ=(hRǷ M}IpU*H?mv+`rs3 sc_cQa>xf4~܃CtjX $zJr#x+I!$m'^ݏlhL!2:"ZsĪxܯ TCxejA7|#8'5w$JCXf1 oPqu]znQ$XabN΃ -#cӌzzj)j5Y]S&H;R.ݲޘψǦ/mYC_l2ò5:ߨq]PO F,7R1wLyB| RBʤ" \>.ˊ-SL.ϣm˽8YA"eiG<ߝS; KD|.cU6sQ,TjʪlǠOMf:~yڶ[Pe|HN N~\Sxe$hزV;rV幬Pm^ b*DH1% j}TBȞBlsQ%=<N$>覆Mͨ2eg O}sv.i$jb6db]H:$]kVxH͌(w#X5Z*#R!Xik_NVW֠("8yݏ!9LH!)dN{sI<UJZx&G};=yφm5JL),TeY˶qL_t\Q l?X ૴N{I 6zNL!J>mXB85Cq鮌C}OqۓSuHsp {sθݸ1}| 㟾`70s\.<}odkdvGP\eU)sMv {ID.(Y&/neY}ՈJ-Q\mίC(τ?ن u_,N8f%@T6:jwZl29@c'êFD p89x*N ,L#>Y$sVV; x$}[ʍH'G()U-4P) V! .͑u!Ip}5G$qCmԓG9Ա@tPj=yUPXc z" 8+'ιu񮑪r`uT2)>xN$2.GUHﯤ%|8ǘcqƦFÎ9=:GpYsBȦR1?<x$Qoquc0F_m ~5Ċv(F9G\T0p;jb86cE`h1Flx*QW,ߍ}P8kD؃h!:% v`jA q])c?libxpertmass-1.4.0/doc/devdoc/libXpertMassCore.h000664 001750 001750 00000005232 15100504560 023047 0ustar00rusconirusconi000000 000000 #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/CalcOptions.hpp" #include "MsXpS/libXpertMassCore/ChemicalGroup.hpp" #include "MsXpS/libXpertMassCore/ChemicalGroupRule.hpp" #include "MsXpS/libXpertMassCore/CleavageAgent.hpp" #include "MsXpS/libXpertMassCore/CleavageConfig.hpp" #include "MsXpS/libXpertMassCore/CleavageMotif.hpp" #include "MsXpS/libXpertMassCore/CleavageRule.hpp" #include "MsXpS/libXpertMassCore/Cleaver.hpp" #include "MsXpS/libXpertMassCore/CrossLinkedRegion.hpp" #include "MsXpS/libXpertMassCore/CrossLinker.hpp" #include "MsXpS/libXpertMassCore/CrossLink.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/FragmentationConfig.hpp" #include "MsXpS/libXpertMassCore/FragmentationPathway.hpp" #include "MsXpS/libXpertMassCore/FragmentationRule.hpp" #include "MsXpS/libXpertMassCore/Fragmenter.hpp" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterShaper.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" #include "MsXpS/libXpertMassCore/MassCollection.hpp" #include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp" #include "MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp" #include "MsXpS/libXpertMassCore/MassDataClient.hpp" #include "MsXpS/libXpertMassCore/MassDataServer.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" #include "MsXpS/libXpertMassCore/MonomerDictionary.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" #include "MsXpS/libXpertMassCore/Oligomer.hpp" #include "MsXpS/libXpertMassCore/OligomerPair.hpp" #include "MsXpS/libXpertMassCore/PkaPhPiDataParser.hpp" #include "MsXpS/libXpertMassCore/PkaPhPi.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/PolChemDefSpec.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Prop.hpp" #include "MsXpS/libXpertMassCore/PropListHolder.hpp" #include "MsXpS/libXpertMassCore/Sequence.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/XpertMassCoreJavaScript.hpp" libxpertmass-1.4.0/doc/devdoc/libXpertMassGui.h000664 001750 001750 00000001753 15100504560 022707 0ustar00rusconirusconi000000 000000 #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassGui/ColorSelector.hpp" #include "MsXpS/libXpertMassGui/DecimalPlacesOptionsDlg.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterGeneratorDlg.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterShaperDlg.hpp" #include "MsXpS/libXpertMassGui/IsotopicDataTableView.hpp" #include "MsXpS/libXpertMassGui/IsotopicDataTableViewModel.hpp" #include "MsXpS/libXpertMassGui/MassDataClientServerConfigDlg.hpp" #include "MsXpS/libXpertMassGui/MassPeakShaperConfigDlg.hpp" #include "MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp" #include "MsXpS/libXpertMassGui/ScriptingEnginesBridge.hpp" #include "MsXpS/libXpertMassGui/ScriptingGuiUtils.hpp" #include "MsXpS/libXpertMassGui/ScriptingHistoryListWidget.hpp" #include "MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp" #include "MsXpS/libXpertMassGui/ScriptingWnd.hpp" #include "MsXpS/libXpertMassGui/ToleranceWidget.hpp" #include "MsXpS/libXpertMassGui/XpertMassGuiJavaScript.hpp" libxpertmass-1.4.0/doc/devdoc/main-page.qdoc000664 001750 001750 00000000504 15100504560 022153 0ustar00rusconirusconi000000 000000 /*! \title \page index.html This developer documentation to the MsXpertSuite libraries contains the following: \list \li \l{MSXpertSuite Module}: The main namespace \li \l{libXpertMassCore Library Module}: The namespace for non-GUI classes \li \l{libXpertMassGui Library Module}: The namespace for GUI classes \endlist */ libxpertmass-1.4.0/doc/devdoc/modules/000775 001750 001750 00000000000 15100507137 021121 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/devdoc/modules/MsXps-module.qdoc000664 001750 001750 00000001126 15100504560 024323 0ustar00rusconirusconi000000 000000 /*! \module MsXpS \title MsXpertSuite Module \brief the MsXpertSuite module is the main module (with namespace MsXpS). The MsXpertSuite module is the main module (with namespace MsXpS) that contains two modules with namespaces MsXpS::libXpertMassCore and MsXpS::libXpertMassGui for non-GUI and GUI classes, respectively: \list \li \l{libXpertMassCore} Library Module: The namespace for non-GUI classes \li \l{libXpertMassGui} Library Module: The namespace for GUI classes \endlist */ /*! \namespace MsXpS \inmodule MsXpSs \brief Contains libXpertMassCore and libXpertMassGui documentation. */ libxpertmass-1.4.0/doc/devdoc/modules/libXpertMassCore-module.qdoc000664 001750 001750 00000002456 15100504560 026506 0ustar00rusconirusconi000000 000000 /*! \module libXpertMassCore \inmodule MsXpS \title libXpertMassCore Library Module \brief Contains non-GUI classes for the modelling of linear polymer chemistries. The libXpertMassCore module provides classes to model all the chemical entities required to fully characterize the behaviour of polymer chemistries both in solution and in the gas phase. It offers both high-level classes such as Polymer and low-level classes such as Isotope. Other classes define how polymers of a given polymer chemistry might be modified, either in solution or in the gas phase (cleavages or fragmentations). \list \li \l{Global variables, enums and functions}, \li \l{The property system}, \li \l{Polymer chemistry definition}, \li \l{Polymer chemistry building blocks}, \li \l{Aqueous chemical reactions}, \li \l{Gas-phase chemical reactions}, \li \l{libXpertMassCore Mass calculations}: configuration of mass calculations,\ isotopic cluster calculations, peak shaping functions, \li \l{libXpertMassCore Utilities}: Network-related classes and other utilities. \endlist */ /*! \namespace MsXpS::libXpertMassCore \inmodule MsXpS \brief Contains the libXpertMassCore documentation. */ /*! \namespace MsXpS::libXpertMassCore::Enums \inmodule MsXpS::libXpertMassCore \brief Contains the libXpertMassCore Enums documentation. */ libxpertmass-1.4.0/doc/devdoc/modules/libXpertMassGui-module.qdoc000664 001750 001750 00000001113 15100504560 026327 0ustar00rusconirusconi000000 000000 /*! \module libXpertMassGui \inmodule MsXpS \title libXpertMassGui Library Module \brief Contains GUI classes. The libXpertMassGui module provides GUI classes for isotopic cluster calculations, automatic selection/deselection of colors for plots, network configuration. \list \li \l{Color selector}, \li \l{libXpertMassGui Mass calculations}: isotopic cluster calculations, peak shaping functions, \li \l{libXpertMassGui Utilities}: Network-related classes. \endlist */ /*! \namespace MsXpS::libXpertMassGui \inmodule MsXpS \brief Contains the libXpertMassGui documentation. */ libxpertmass-1.4.0/doc/javascript/000775 001750 001750 00000000000 15100507137 020353 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/CMakeLists.txt000664 001750 001750 00000007237 15100504560 023121 0ustar00rusconirusconi000000 000000 message("\n${BoldGreen}Now configuring src/doc/javascript for ${PROJECT_NAME}${ColourReset}\n") ################ JavaScript reference text handling ################# ################ JavaScript reference text handling ################# find_package(Python3 REQUIRED) ############ List all the files that contain the class JS reference tag # that is like so: # /* BEGIN CLASS JS REFERENCE # * namespace: pappso # * class name: MzIntegrationParams # */ # The jsExtract.py program lists the files to stdout and that output # is redirected to OUTPUT_FILE. set(TOP_JS_REF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/js_reference) set(SCRIPTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/scripts) set(CORE_SRC_DIR ${CMAKE_SOURCE_DIR}/source/XpertMassCore) set(GUI_SRC_DIR ${CMAKE_SOURCE_DIR}/source/XpertMassGui) set(CORE_OUTPUT_DIR ${TOP_JS_REF_DIR}/Core) set(GUI_OUTPUT_DIR ${TOP_JS_REF_DIR}/Gui) set(CORE_CLASS_JS_REF_FILE_LIST ${CORE_OUTPUT_DIR}/class_js_reference_file_list.txt) set(CORE_CLASS_JS_REF_TEXT ${CORE_OUTPUT_DIR}/class_js_reference_text.txt) set(GUI_CLASS_JS_REF_FILE_LIST ${GUI_OUTPUT_DIR}/class_js_reference_file_list.txt) set(GUI_CLASS_JS_REF_TEXT ${GUI_OUTPUT_DIR}/class_js_reference_text.txt) # When run (see add_custom_target(list_class_js_ref_files below) # this command will list all the header files in the Core lib that have # a class JS reference text in them. add_custom_command( OUTPUT ${CORE_CLASS_JS_REF_FILE_LIST} COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/jsExtract.py --list-files class -d ${CORE_SRC_DIR} -t " " > ${CORE_CLASS_JS_REF_FILE_LIST} COMMENT "List Core files containing class JS reference text" VERBATIM ) # When run (see add_custom_target(list_class_js_ref_files below) # this command will list all the header files in the Widget lib that have # a class JS reference text in them. add_custom_command( OUTPUT ${GUI_CLASS_JS_REF_FILE_LIST} COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/jsExtract.py --list-files class -d ${GUI_SRC_DIR} -t " " > ${GUI_CLASS_JS_REF_FILE_LIST} COMMENT "List Widget files containing class JS reference text" VERBATIM ) # Runs the commands above that create the files listed here as DEPENDS add_custom_target(list_class_js_ref_files DEPENDS ${CORE_CLASS_JS_REF_FILE_LIST} ${GUI_CLASS_JS_REF_FILE_LIST} COMMENT "Listing Core and Widget files containing class JS reference text" ) # When run (see add_custom_target(extract_class_js_ref_text below) # this command will extract from the files listed in the lists generated above # all the class JS reference text. # # Core add_custom_command( OUTPUT ${CORE_CLASS_JS_REF_TEXT} DEPENDS ${CORE_CLASS_JS_REF_FILE_LIST} COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/jsExtract.py --tab " " --extract class --listfile ${CORE_CLASS_JS_REF_FILE_LIST} > ${CORE_CLASS_JS_REF_TEXT} COMMENT "Extract class JS reference text from Core header files" VERBATIM ) # # Widget add_custom_command( OUTPUT ${GUI_CLASS_JS_REF_TEXT} DEPENDS ${GUI_CLASS_JS_REF_FILE_LIST} COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/jsExtract.py --tab " " --extract class --listfile ${GUI_CLASS_JS_REF_FILE_LIST} > ${GUI_CLASS_JS_REF_TEXT} COMMENT "Extract class JS reference text from Widget header files" VERBATIM ) # Runs the commands above that create the files listed here as DEPENDS add_custom_target(extract_class_js_ref_text DEPENDS ${CORE_CLASS_JS_REF_TEXT} ${GUI_CLASS_JS_REF_TEXT} COMMENT "Extracting class JavaScript reference text into ${CORE_CLASS_JS_REF_TEXT} and ${GUI_CLASS_JS_REF_TEXT}" ) message("\n${BoldGreen}Done configuring src/doc/javascript for ${PROJECT_NAME}${ColourReset}\n") libxpertmass-1.4.0/doc/javascript/js_reference/000775 001750 001750 00000000000 15100507137 023005 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Core/000775 001750 001750 00000000000 15100507137 023675 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Core/class_js_reference_file_list.txt000664 001750 001750 00000002465 15100504560 032313 0ustar00rusconirusconi000000 000000 /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CalcOptions.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IndexRange.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IndexRangeCollection.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicData.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Polymer.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Utils.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageAgent.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageConfig.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Formula.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Ionizer.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Isotope.hpp libxpertmass-1.4.0/doc/javascript/js_reference/Core/class_js_reference_text.txt000664 001750 001750 00000075117 15100504560 031331 0ustar00rusconirusconi000000 000000 MsXpS::libXpertMassCore CalcOptions: Properties: 6 bool isDeepCalculation Enums::MassType massType Enums::CapType capType Enums::ChemicalEntity monomerEntities Enums::ChemicalEntity polymerEntities Enums::CapType selectionType Invokables: 28 explicit CalcOptions(QObject *parent = nullptr); explicit CalcOptions(bool deep_calculation, Enums::MassType mass_type, Enums::CapType selectionping, Enums::ChemicalEntity monomer_entities, Enums::ChemicalEntity polymer_entities, QObject *parent = nullptr); explicit CalcOptions(const CalcOptions &other, QObject *parent = nullptr); CalcOptions &initialize(const CalcOptions &other); CalcOptions *clone(QObject *parent = nullptr); static CalcOptions *clone(const CalcOptions &other, QObject *parent = nullptr); void setIndexRange(const IndexRange &sequence_range = IndexRange()); void setIndexRange(qsizetype index_start, qsizetype index_end); void setIndexRanges(const IndexRangeCollection &sequence_ranges); const IndexRangeCollection &getIndexRangeCollectionCstRef() const; IndexRangeCollection &getIndexRangeCollectionRef(); IndexRangeCollection *getIndexRangeCollection(); void setDeepCalculation(bool deep); bool isDeepCalculation() const; void setMassType(Enums::MassType mass_type); Enums::MassType getMassType() const; void setSelectionType(Enums::SelectionType selection_type); Enums::SelectionType getSelectionType() const; void setCapType(Enums::CapType selection_type); Enums::CapType getCapType() const; void setMonomerEntities(Enums::ChemicalEntity monomer_chem_ent); Enums::ChemicalEntity getMonomerEntities() const; void setPolymerEntities(Enums::ChemicalEntity polymer_chem_ent); Enums::ChemicalEntity getPolymerEntities() const; CalcOptions &operator=(const CalcOptions &other) = delete; bool operator==(const CalcOptions &other) const; bool operator!=(const CalcOptions &other) const; QString toString() const; MsXpS::libXpertMassCore IndexRange: Properties: 2 qsizetype start qsizetype stop Invokables: 12 explicit IndexRange(QObject *parent = nullptr); explicit IndexRange(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); explicit IndexRange(const IndexRange &other, QObject *parent = nullptr); void initialize(const IndexRange &other); IndexRange *clone(QObject *parent = nullptr); static IndexRange *clone(const IndexRange &other, QObject *parent = nullptr); bool operator==(const IndexRange &other) const; bool operator!=(const IndexRange &other) const; void sortAscending(); void sortDescending(); bool isValid() const; void reset(); MsXpS::libXpertMassCore IndexRangeCollection: Properties: 1 QString comment Invokables: 29 explicit IndexRangeCollection(QObject *parent = nullptr); explicit IndexRangeCollection(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); explicit IndexRangeCollection( const QString &index_ranges_string, Enums::LocationType location_type = Enums::LocationType::INDEX, QObject *parent = nullptr); explicit IndexRangeCollection(const IndexRangeCollection &other, QObject *parent = nullptr); const QList &getRangesCstRef() const; QList &getRangesRef(); static QList parseIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type, QObject *parent = nullptr); bool operator==(const IndexRangeCollection &other) const; bool operator!=(const IndexRangeCollection &other) const; void setIndexRange(qsizetype start, qsizetype stop); void setIndexRange(const IndexRange &index_range); void setIndexRanges(const QList &index_ranges); void setIndexRanges(const IndexRangeCollection &index_ranges); qsizetype setIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type); void appendIndexRange(qsizetype start, qsizetype stop); void appendIndexRange(const IndexRange &index_range); void appendIndexRanges(const QList &index_ranges); void appendIndexRanges(const IndexRangeCollection &index_ranges); qsizetype leftMostIndexRangeStart() const; QList indicesOfLeftMostIndexRanges() const; bool isLeftMostIndexRange(const IndexRange &index_range) const; qsizetype rightMostIndexRangeStop() const; QList indicesOfRightMostIndexRanges() const; bool isRightMostIndexRange(const IndexRange &index_range) const; IndexRange *mostInclusiveLeftRightIndexRange() const; bool encompassIndex(qsizetype index, bool globally = false) const; bool overlap() const; QString indicesAsText() const; QString positionsAsText() const; MsXpS::libXpertMassCore IsotopicData: Properties: 1 std::size_t size Invokables: 22 IsotopicData(QObject *parent = nullptr); IsotopicData(const IsotopicData &other, QObject *parent = nullptr); void appendNewIsotope(IsotopeQSPtr isotope_qsp, bool update_maps = true); bool insertNewIsotope(IsotopeQSPtr isotope_qsp, qsizetype index, bool update_maps = true); void appendNewIsotopes(const QList &isotopes, bool update_maps = true); bool updateMonoMassMap(const QString &symbol); std::size_t updateMonoMassMap(); bool updateAvgMassMap(const QString &symbol); std::size_t updateAvgMassMap(); double computeAvgMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; void updateMassMaps(const QString &symbol); std::size_t updateMassMaps(); double getMonoMassBySymbol(const QString &symbol, bool &ok) const; double getMonoMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; double getAvgMassBySymbol(const QString &symbol, bool &ok) const; double getCumulatedProbabilitiesBySymbol(const QString &symbol, ErrorList *error_list_p) const; double getCumulatedProbabilities(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; qsizetype getIsotopeCountBySymbol(const QString &symbol) const; bool containsSymbol(const QString &symbol, int &count) const; bool containsName(const QString &name, int &count) const; QString isotopesAsStringBySymbol(const QString &symbol) const; bool isMonoMassIsotope(IsotopeCstQSPtr isotope_cqsp); MsXpS::libXpertMassCore MassPeakShaperConfig: Properties: 11 double resolution double fwhm Enums::MassPeakWidthLogic massPeakWidthLogic double referencePeakMz int pointCount bool withBins int binSizeDivisor double binSize bool isBinSizeFixed double mzStep Enums::MassPeakShapeType massPeakShapeType MsXpS::libXpertMassCore Polymer: Properties: 6 QString name QString code QString author QString filePath QString sequence std::size_t size Invokables: 45 void setName(const QString &name); QString getName() const; void setCode(const QString &code); QString getCode() const; void setAuthor(const QString &author); QString getAuthor() const; void setFilePath(const QString &file_path); QString getFilePath() const; void setDateTime(); void setDateTime(const QString &date_time); QString getDateTime() const; void setSequence(const QString &sequence_string); QString getSequenceText() const; std::size_t size() const; bool modifyMonomer(std::size_t index, const QString modif_name, bool override); bool hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const; void unmodify(Enums::PolymerEnd polymer_end); bool setLeftEndModifByName(const QString &name = QString()); bool setRightEndModifByName(const QString &name = QString()); void setCalcOptions(const CalcOptions &calc_options); void setCalcOptions(const CalcOptions *calc_options); CalcOptions *getCalcOptions(); void setIonizer(const Ionizer *ionizer_p); Ionizer *getIonizer(); Enums::IonizationOutcome ionize(); Enums::IonizationOutcome ionize(const Ionizer &ionizer); Enums::IonizationOutcome deionize(); Enums::IonizationOutcome deionize(const Ionizer &ionizer); Enums::IonizationOutcome molecularMasses(double &mono, double &avg) const; std::size_t crossLinkCount() const; static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg, bool reset); static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, const Ionizer &ionizer, double &mono, double &avg); bool calculateMasses(const CalcOptions &calc_options, bool reset); bool calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer); bool calculateMasses(const CalcOptions *calc_options_p, const Ionizer *ionizer_p); bool calculateMasses(); double getMass(Enums::MassType mass_type) const; double &getMassRef(Enums::MassType mass_type); QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options) const; QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options, const Ionizer &ionizer) const; QString elementalComposition(const CalcOptions &calc_options) const; QString elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const; bool isValid() const; QString toString() const; void clear(); MsXpS::libXpertMassCore Utils: Description: This class can be used in JS in two ways: 1. By direct use of the 'utils' singleton and directly calling the invokable functions like so: utils.unspacify("this text has spaces") 2. By instantiating an object like so: var the_utils = new Utils() and later calling the functions as methods of the object like so: the_utils.unspacify("this text has spaces") Properties: 1 QString xmlIndentationToken Defines the string used for XML indentation formatting Invokables: 17 explicit Utils(QObject *parent = nullptr); Creates a new instance of Utils static void doubleListStatistics(QList list, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &greatest, double &median); Given the list of double values sets a number of statistical * values static bool almostEqual(double value1, double value2, int decimalPlaces = 10); Tells if two double values can be told apart with the * precision of the computer. static QString unspacify(const QString &text); Remove any space anywhere in the text string. static QString binaryRepresentation(int value); Returns the binary representation of the value. static QString elideText(const QString &text, int charsLeft = 4, int charsRight = 4, const QString &delimitor = "..."); static QString stanzify(const QString &text, int width); static QString stanzifyParagraphs(const QString &text, int width); static int countZeroDecimals(double value); static double ppm(double value, double ppm); Returns the mass delta corresponding to ppm of value. static double addPpm(double value, double ppm); static double removePpm(double value, double ppm); static double res(double value, double res); static double addRes(double value, double res); static double removeRes(double value, double res); static QString joinErrorList(const ErrorList &error_list, const QString &separator = "\n"); static bool isProgramRunning(const QString &program_name); MsXpS::libXpertMassCore CleavageAgent: MsXpS::libXpertMassCore CleavageConfig: Properties: 4 int partials int startIonizeLevel int stopIonizeLevel bool sequenceEmbedded Invokables: 7 CleavageConfig(QObject *parent = nullptr); CleavageConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &pattern, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); CleavageConfig(const CleavageAgent &cleavage_agent, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); CleavageConfig(const CleavageConfig &other, QObject *parent = nullptr); bool initialize(const CleavageConfig &other); bool setCleavageAgent(const CleavageAgent &cleavage_agent); void setIonizeLevels(int value1, int value2); MsXpS::libXpertMassCore Formula: Properties: 3 QString title QString actionFormula bool valid Invokables: 23 explicit Formula(QObject *parent = nullptr); explicit Formula(const QString &formula_string, QObject *parent = nullptr); Formula *clone(const Formula &other, QObject *parent = nullptr); Formula &initialize(const Formula &other); void setActionFormula(const QString &formula); void setActionFormula(const Formula &formula); bool appendActionFormula(const QString &formula); QString getActionFormula(bool withTitle = false) const; void setTitle(const QString &title); QString getTitle() const; QString extractTitle() const; QString removeTitle(); void setForceCountIndex(bool forceCountIndex); bool isForceCountIndex() const; static bool checkSyntax(const QString &formula, bool forceCountIndex = false); bool checkSyntax() const; static QChar actions(const QString &formula); QChar actions() const; bool hasNetMinusPart(); QString getPlusFormula() const; QString getMinusFormula() const; QString elementalComposition( std::vector> *symbol_count_pairs_p = nullptr) const; QString formatXmlFormulaElement( int offset, const QString &indent = Utils::xmlIndentationToken); MsXpS::libXpertMassCore Ionizer: Properties: 6 int nominalCharge int level bool isValid int currentStateNominalCharge int currentStateLevel bool isCurrentStateValid Invokables: 8 Ionizer(QObject *parent = nullptr); int charge() const; int currentStateCharge() const; void forceCurrentState(const Formula &formula, int nominal_charge, int level); void forceCurrentStateLevel(int level); Ionizer makeIonizerWithCurrentStateData(); bool validate(ErrorList *error_list_p) const; bool validateCurrentState(ErrorList *error_list_p) const; MsXpS::libXpertMassCore Isotope: Properties: 5 QString name QString symbol double mass double probability bool isValid Invokables: 17 Isotope(QObject *parent = nullptr); Isotope(const QString &name, const QString &symbol, double mass, double probability, QObject *parent = nullptr); Isotope(const QString &text, QObject *parent = nullptr); Isotope *clone(QObject *parent = nullptr) const; bool initialize(const QString &name, const QString &symbol, double mass, double probability); bool initialize(const QString &text); void clear(); void setName(const QString &name); QString getName() const; void setSymbol(const QString &symbol); QString getSymbol() const; void setMass(double mass); double getMass() const; void setProbability(double probability); double getProbability() const; bool validate(ErrorList *error_list_p); QString toString() const; MsXpS::libXpertMassCore CalcOptions: Properties: 6 bool isDeepCalculation Enums::MassType massType Enums::CapType capType Enums::ChemicalEntity monomerEntities Enums::ChemicalEntity polymerEntities Enums::CapType selectionType Invokables: 28 explicit CalcOptions(QObject *parent = nullptr); explicit CalcOptions(bool deep_calculation, Enums::MassType mass_type, Enums::CapType selectionping, Enums::ChemicalEntity monomer_entities, Enums::ChemicalEntity polymer_entities, QObject *parent = nullptr); explicit CalcOptions(const CalcOptions &other, QObject *parent = nullptr); CalcOptions &initialize(const CalcOptions &other); CalcOptions *clone(QObject *parent = nullptr); static CalcOptions *clone(const CalcOptions &other, QObject *parent = nullptr); void setIndexRange(const IndexRange &sequence_range = IndexRange()); void setIndexRange(qsizetype index_start, qsizetype index_end); void setIndexRanges(const IndexRangeCollection &sequence_ranges); const IndexRangeCollection &getIndexRangeCollectionCstRef() const; IndexRangeCollection &getIndexRangeCollectionRef(); IndexRangeCollection *getIndexRangeCollection(); void setDeepCalculation(bool deep); bool isDeepCalculation() const; void setMassType(Enums::MassType mass_type); Enums::MassType getMassType() const; void setSelectionType(Enums::SelectionType selection_type); Enums::SelectionType getSelectionType() const; void setCapType(Enums::CapType selection_type); Enums::CapType getCapType() const; void setMonomerEntities(Enums::ChemicalEntity monomer_chem_ent); Enums::ChemicalEntity getMonomerEntities() const; void setPolymerEntities(Enums::ChemicalEntity polymer_chem_ent); Enums::ChemicalEntity getPolymerEntities() const; CalcOptions &operator=(const CalcOptions &other) = delete; bool operator==(const CalcOptions &other) const; bool operator!=(const CalcOptions &other) const; QString toString() const; MsXpS::libXpertMassCore IndexRange: Properties: 2 qsizetype start qsizetype stop Invokables: 12 explicit IndexRange(QObject *parent = nullptr); explicit IndexRange(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); explicit IndexRange(const IndexRange &other, QObject *parent = nullptr); void initialize(const IndexRange &other); IndexRange *clone(QObject *parent = nullptr); static IndexRange *clone(const IndexRange &other, QObject *parent = nullptr); bool operator==(const IndexRange &other) const; bool operator!=(const IndexRange &other) const; void sortAscending(); void sortDescending(); bool isValid() const; void reset(); MsXpS::libXpertMassCore IndexRangeCollection: Properties: 1 QString comment Invokables: 29 explicit IndexRangeCollection(QObject *parent = nullptr); explicit IndexRangeCollection(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); explicit IndexRangeCollection( const QString &index_ranges_string, Enums::LocationType location_type = Enums::LocationType::INDEX, QObject *parent = nullptr); explicit IndexRangeCollection(const IndexRangeCollection &other, QObject *parent = nullptr); const QList &getRangesCstRef() const; QList &getRangesRef(); static QList parseIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type, QObject *parent = nullptr); bool operator==(const IndexRangeCollection &other) const; bool operator!=(const IndexRangeCollection &other) const; void setIndexRange(qsizetype start, qsizetype stop); void setIndexRange(const IndexRange &index_range); void setIndexRanges(const QList &index_ranges); void setIndexRanges(const IndexRangeCollection &index_ranges); qsizetype setIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type); void appendIndexRange(qsizetype start, qsizetype stop); void appendIndexRange(const IndexRange &index_range); void appendIndexRanges(const QList &index_ranges); void appendIndexRanges(const IndexRangeCollection &index_ranges); qsizetype leftMostIndexRangeStart() const; QList indicesOfLeftMostIndexRanges() const; bool isLeftMostIndexRange(const IndexRange &index_range) const; qsizetype rightMostIndexRangeStop() const; QList indicesOfRightMostIndexRanges() const; bool isRightMostIndexRange(const IndexRange &index_range) const; IndexRange *mostInclusiveLeftRightIndexRange() const; bool encompassIndex(qsizetype index, bool globally = false) const; bool overlap() const; QString indicesAsText() const; QString positionsAsText() const; MsXpS::libXpertMassCore IsotopicData: Properties: 1 std::size_t size Invokables: 22 IsotopicData(QObject *parent = nullptr); IsotopicData(const IsotopicData &other, QObject *parent = nullptr); void appendNewIsotope(IsotopeQSPtr isotope_qsp, bool update_maps = true); bool insertNewIsotope(IsotopeQSPtr isotope_qsp, qsizetype index, bool update_maps = true); void appendNewIsotopes(const QList &isotopes, bool update_maps = true); bool updateMonoMassMap(const QString &symbol); std::size_t updateMonoMassMap(); bool updateAvgMassMap(const QString &symbol); std::size_t updateAvgMassMap(); double computeAvgMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; void updateMassMaps(const QString &symbol); std::size_t updateMassMaps(); double getMonoMassBySymbol(const QString &symbol, bool &ok) const; double getMonoMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; double getAvgMassBySymbol(const QString &symbol, bool &ok) const; double getCumulatedProbabilitiesBySymbol(const QString &symbol, ErrorList *error_list_p) const; double getCumulatedProbabilities(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; qsizetype getIsotopeCountBySymbol(const QString &symbol) const; bool containsSymbol(const QString &symbol, int &count) const; bool containsName(const QString &name, int &count) const; QString isotopesAsStringBySymbol(const QString &symbol) const; bool isMonoMassIsotope(IsotopeCstQSPtr isotope_cqsp); MsXpS::libXpertMassCore MassPeakShaperConfig: Properties: 11 double resolution double fwhm Enums::MassPeakWidthLogic massPeakWidthLogic double referencePeakMz int pointCount bool withBins int binSizeDivisor double binSize bool isBinSizeFixed double mzStep Enums::MassPeakShapeType massPeakShapeType MsXpS::libXpertMassCore Polymer: Properties: 6 QString name QString code QString author QString filePath QString sequence std::size_t size Invokables: 45 void setName(const QString &name); QString getName() const; void setCode(const QString &code); QString getCode() const; void setAuthor(const QString &author); QString getAuthor() const; void setFilePath(const QString &file_path); QString getFilePath() const; void setDateTime(); void setDateTime(const QString &date_time); QString getDateTime() const; void setSequence(const QString &sequence_string); QString getSequenceText() const; std::size_t size() const; bool modifyMonomer(std::size_t index, const QString modif_name, bool override); bool hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const; void unmodify(Enums::PolymerEnd polymer_end); bool setLeftEndModifByName(const QString &name = QString()); bool setRightEndModifByName(const QString &name = QString()); void setCalcOptions(const CalcOptions &calc_options); void setCalcOptions(const CalcOptions *calc_options); CalcOptions *getCalcOptions(); void setIonizer(const Ionizer *ionizer_p); Ionizer *getIonizer(); Enums::IonizationOutcome ionize(); Enums::IonizationOutcome ionize(const Ionizer &ionizer); Enums::IonizationOutcome deionize(); Enums::IonizationOutcome deionize(const Ionizer &ionizer); Enums::IonizationOutcome molecularMasses(double &mono, double &avg) const; std::size_t crossLinkCount() const; static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg, bool reset); static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, const Ionizer &ionizer, double &mono, double &avg); bool calculateMasses(const CalcOptions &calc_options, bool reset); bool calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer); bool calculateMasses(const CalcOptions *calc_options_p, const Ionizer *ionizer_p); bool calculateMasses(); double getMass(Enums::MassType mass_type) const; double &getMassRef(Enums::MassType mass_type); QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options) const; QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options, const Ionizer &ionizer) const; QString elementalComposition(const CalcOptions &calc_options) const; QString elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const; bool isValid() const; QString toString() const; void clear(); MsXpS::libXpertMassCore Utils: Description: This class can be used in JS in two ways: 1. By direct use of the 'utils' singleton and directly calling the invokable functions like so: utils.unspacify("this text has spaces") 2. By instantiating an object like so: var the_utils = new Utils() and later calling the functions as methods of the object like so: the_utils.unspacify("this text has spaces") Properties: 1 QString xmlIndentationToken Defines the string used for XML indentation formatting Invokables: 17 explicit Utils(QObject *parent = nullptr); Creates a new instance of Utils static void doubleListStatistics(QList list, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &greatest, double &median); Given the list of double values sets a number of statistical * values static bool almostEqual(double value1, double value2, int decimalPlaces = 10); Tells if two double values can be told apart with the * precision of the computer. static QString unspacify(const QString &text); Remove any space anywhere in the text string. static QString binaryRepresentation(int value); Returns the binary representation of the value. static QString elideText(const QString &text, int charsLeft = 4, int charsRight = 4, const QString &delimitor = "..."); static QString stanzify(const QString &text, int width); static QString stanzifyParagraphs(const QString &text, int width); static int countZeroDecimals(double value); static double ppm(double value, double ppm); Returns the mass delta corresponding to ppm of value. static double addPpm(double value, double ppm); static double removePpm(double value, double ppm); static double res(double value, double res); static double addRes(double value, double res); static double removeRes(double value, double res); static QString joinErrorList(const ErrorList &error_list, const QString &separator = "\n"); static bool isProgramRunning(const QString &program_name); MsXpS::libXpertMassCore CleavageConfig: Properties: 4 int partials int startIonizeLevel int stopIonizeLevel bool sequenceEmbedded Invokables: 7 CleavageConfig(QObject *parent = nullptr); CleavageConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &pattern, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); CleavageConfig(const CleavageAgent &cleavage_agent, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); CleavageConfig(const CleavageConfig &other, QObject *parent = nullptr); bool initialize(const CleavageConfig &other); bool setCleavageAgent(const CleavageAgent &cleavage_agent); void setIonizeLevels(int value1, int value2); MsXpS::libXpertMassCore Formula: Properties: 3 QString title QString actionFormula bool valid Invokables: 23 explicit Formula(QObject *parent = nullptr); explicit Formula(const QString &formula_string, QObject *parent = nullptr); Formula *clone(const Formula &other, QObject *parent = nullptr); Formula &initialize(const Formula &other); void setActionFormula(const QString &formula); void setActionFormula(const Formula &formula); bool appendActionFormula(const QString &formula); QString getActionFormula(bool withTitle = false) const; void setTitle(const QString &title); QString getTitle() const; QString extractTitle() const; QString removeTitle(); void setForceCountIndex(bool forceCountIndex); bool isForceCountIndex() const; static bool checkSyntax(const QString &formula, bool forceCountIndex = false); bool checkSyntax() const; static QChar actions(const QString &formula); QChar actions() const; bool hasNetMinusPart(); QString getPlusFormula() const; QString getMinusFormula() const; QString elementalComposition( std::vector> *symbol_count_pairs_p = nullptr) const; QString formatXmlFormulaElement( int offset, const QString &indent = Utils::xmlIndentationToken); MsXpS::libXpertMassCore Ionizer: Properties: 6 int nominalCharge int level bool isValid int currentStateNominalCharge int currentStateLevel bool isCurrentStateValid Invokables: 8 Ionizer(QObject *parent = nullptr); int charge() const; int currentStateCharge() const; void forceCurrentState(const Formula &formula, int nominal_charge, int level); void forceCurrentStateLevel(int level); Ionizer makeIonizerWithCurrentStateData(); bool validate(ErrorList *error_list_p) const; bool validateCurrentState(ErrorList *error_list_p) const; MsXpS::libXpertMassCore Isotope: Properties: 5 QString name QString symbol double mass double probability bool isValid Invokables: 17 Isotope(QObject *parent = nullptr); Isotope(const QString &name, const QString &symbol, double mass, double probability, QObject *parent = nullptr); Isotope(const QString &text, QObject *parent = nullptr); Isotope *clone(QObject *parent = nullptr) const; bool initialize(const QString &name, const QString &symbol, double mass, double probability); bool initialize(const QString &text); void clear(); void setName(const QString &name); QString getName() const; void setSymbol(const QString &symbol); QString getSymbol() const; void setMass(double mass); double getMass() const; void setProbability(double probability); double getProbability() const; bool validate(ErrorList *error_list_p); QString toString() const; libxpertmass-1.4.0/doc/javascript/js_reference/Core/extraction_error.txt000664 001750 001750 00000000000 15100504560 030012 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Core/file_listing_error.txt000664 001750 001750 00000000000 15100504560 030302 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Gui/000775 001750 001750 00000000000 15100507137 023531 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Gui/class_js_reference_file_list.txt000664 001750 001750 00000000755 15100504560 032147 0ustar00rusconirusconi000000 000000 /home/rusconi/devel/xpertmass/development/source/XpertMassGui/includes/MsXpS/libXpertMassGui/DecimalPlacesOptionsDlg.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassDataClientServerConfigDlg.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassPeakShaperConfigDlg.hpp /home/rusconi/devel/xpertmass/development/source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp libxpertmass-1.4.0/doc/javascript/js_reference/Gui/class_js_reference_text.txt000664 001750 001750 00000006474 15100504560 031165 0ustar00rusconirusconi000000 000000 MsXpS::libXpertMassGui DecimalPlacesOptionsDlg: Description: This class allows the user to configure the number of decimals to be used when textually representing the mass values of various chemical entities. Properties: 4 int atomDecPlaces int oligomerDecPlaces int polymerDecPlaces int pkaPhPiDecPlaces Invokables: 1 void updateDecimalPlaces(int atom, int oligo, int poly, int pka_ph); MsXpS::libXpertMassGui MassDataClientServerConfigDlg: Properties: 2 std::pair std::pair MsXpS::libXpertMassGui MassPeakShaperConfigDlg: Invokables: 2 void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); MassPeakShaperConfigWidget *getConfigWidget(); MsXpS::libXpertMassGui MassPeakShaperConfigWidget: Properties: 3 double referencePeakMz libXpertMassCore::MassPeakShaperConfig *config double normalizingIntensity Invokables: 7 libXpertMassCore::MassPeakShaperConfig *getConfig(); bool processBinSizeConfig(); void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); void setBinSizeManually(double bin_size, bool should_create_bins = false); bool checkParameters(); bool checkParameters(libXpertMassCore::ErrorList *error_list); const libXpertMassCore::MassPeakShaperConfig *getConfig() const; MsXpS::libXpertMassGui DecimalPlacesOptionsDlg: Description: This class allows the user to configure the number of decimals to be used when textually representing the mass values of various chemical entities. Properties: 4 int atomDecPlaces int oligomerDecPlaces int polymerDecPlaces int pkaPhPiDecPlaces Invokables: 1 void updateDecimalPlaces(int atom, int oligo, int poly, int pka_ph); MsXpS::libXpertMassGui MassDataClientServerConfigDlg: Properties: 2 std::pair std::pair MsXpS::libXpertMassGui MassPeakShaperConfigDlg: Invokables: 2 void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); MassPeakShaperConfigWidget *getConfigWidget(); MsXpS::libXpertMassGui MassPeakShaperConfigWidget: Properties: 3 double referencePeakMz libXpertMassCore::MassPeakShaperConfig *config double normalizingIntensity Invokables: 7 libXpertMassCore::MassPeakShaperConfig *getConfig(); bool processBinSizeConfig(); void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); void setBinSizeManually(double bin_size, bool should_create_bins = false); bool checkParameters(); bool checkParameters(libXpertMassCore::ErrorList *error_list); const libXpertMassCore::MassPeakShaperConfig *getConfig() const; libxpertmass-1.4.0/doc/javascript/js_reference/Gui/extraction_error.txt000664 001750 001750 00000000000 15100504560 027646 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/js_reference/Gui/file_listing_error.txt000664 001750 001750 00000000000 15100504560 030136 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/scripts/000775 001750 001750 00000000000 15100507137 022042 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/scripts/Attention-these-files-are-hardlinks000664 001750 001750 00000000000 15100504560 030645 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/doc/javascript/scripts/ClassJsRef.py000664 001750 001750 00000005237 15100504560 024417 0ustar00rusconirusconi000000 000000 '''This ClassJsRef module provides JS reference for a given C++ class''' class ClassJsRef: '''This class acts as a storage unit for a class JS reference''' def __init__(self): self.name_space = "" self.class_name = "" # List of lines making the description self.description = [ ] # List of QpropertyJsRef instances self.properties = [ ] # List of QinvokableJsRef instances self.invokables = [ ] self.valid = False def __str__(self): return self.get_as_text(" ", 1) def get_description_as_text(self, tab, offset): lead = offset * tab # print(f"The description: {self.description}\n") # print(f"The description has {len(self.description)} items.") output_text = "" for line in self.description: # print(f"Appending class description line: '{line}") output_text = f"{output_text}{lead}{line}" # print(f"Now output_text is:\n{output_text}") return output_text def print_description(self, tab, offset): print(f"{self.get_description_as_text(tab, offset)}") def get_properties_as_text(self, tab, offset): output_text = "" for property in self.properties: output_text = f"{output_text}{property.get_as_text(tab, offset)}" # print(f"Returning properties as text: {output_text}") return output_text def print_properties(self, tab, offset): print(f"{self.get_properties_as_text(tab, offset)}") def get_invokables_as_text(self, tab, offset): output_text = "" for invokable in self.invokables: output_text = f"{output_text}{invokable.get_as_text(tab, offset)}" return output_text def print_invokables(self, tab, offset): print(f"{self.get_invokables_as_text(tab, offset)}") def get_as_text(self, tab, offset): lead = offset * tab output_text = f"{lead}{self.name_space} {self.class_name}:\n\n" offset += 1 lead = offset * tab if len(self.description): output_text = f"{output_text}{lead}Description:\n\n" indented_offset = offset + 1 output_text = f"{output_text}{self.get_description_as_text(tab, indented_offset)}\n" if len(self.properties): output_text = f"{output_text}{lead}Properties: {len(self.properties)}\n\n" indented_offset = offset + 1 output_text = f"{output_text}{self.get_properties_as_text(tab, indented_offset)}\n" #print(f"Got {output_text} for properties text.") if len(self.invokables): output_text = f"{output_text}{lead}Invokables: {len(self.invokables)}\n\n" indented_offset = offset + 1 output_text = f"{output_text}{self.get_invokables_as_text(tab, indented_offset)}\n" # print(f"Got {output_text} for invokables text.") return output_text def print(self, tab, offset): print(f"{self.get_as_text(tab, offset)}") libxpertmass-1.4.0/doc/javascript/scripts/QinvokableJsRef.py000664 001750 001750 00000002565 15100504560 025446 0ustar00rusconirusconi000000 000000 '''This QinvokableJsRef module provides JS reference for a given C++ class' Q_INVOKABLE statements''' class QinvokableJsRef: '''This class acts as a storage unit for a class JS reference''' def __init__(self): self.name_space = "" self.class_name = "" # The C++ statement extracted from the code self.statement = "" # List of lines making the description self.description = [ ] def __str__(self): return self.get_as_text(" ", 1) def get_description_as_text(self, tab, offset): lead = offset * tab output_text = "" for line in self.description: output_text = f"{output_text}{lead}{line}\n" # print(f"Returning invokable description:\n{output_text}") return output_text def print_description(self, tab, offset): output_text = self.get_description_as_text(tab, offset) return output_text def get_as_text(self, tab, offset): lead = offset * tab output_text = f"{lead}{self.statement}\n" if len(self.description): indent_offset = offset + 1 # lead = indent_offset * tab # output_text = f"{output_text}{lead}Description:\n" # indent_offset += 1 output_text = f"{output_text}{self.get_description_as_text(tab, indent_offset)}\n" # print(f"Returning QinvokableJsRef as text:\n{output_text}") return output_text def print(self, tab, offset): output_text = self.get_as_text(tab, offset) print(output_text) libxpertmass-1.4.0/doc/javascript/scripts/QpropertyJsRef.py000664 001750 001750 00000002563 15100504560 025356 0ustar00rusconirusconi000000 000000 '''This QpropertyJsRef module provides JS reference for a given C++ class' Q_PROPERTY statements''' class QpropertyJsRef: '''This class acts as a storage unit for a class JS reference''' def __init__(self): self.name_space = "" self.class_name = "" # The C++ statement extracted from the code self.statement = "" # List of lines making the description self.description = [ ] def __str__(self): return self.get_as_text(" ", 1) def get_description_as_text(self, tab, offset): lead = offset * tab output_text = "" for line in self.description: output_text = f"{output_text}{lead}{line}\n" # print(f"Returning property description:\n{output_text}") return output_text def print_description(self, tab, offset): output_text = self.get_description_as_text(tab, offset) return output_text def get_as_text(self, tab, offset): lead = offset * tab output_text = f"{lead}{self.statement}\n" if len(self.description): indent_offset = offset + 1 # lead = indent_offset * tab # output_text = f"{output_text}{lead}Description:\n" # indent_offset += 1 output_text = f"{output_text}{self.get_description_as_text(tab, indent_offset)}\n" # print(f"Returning QpropertyJsRef as text:\n{output_text}") return output_text def print(self, tab, offset): output_text = self.get_as_text(tab, offset) print(output_text) libxpertmass-1.4.0/doc/javascript/scripts/jsExtract.py000775 001750 001750 00000011664 15100504560 024373 0ustar00rusconirusconi000000 000000 #!/usr/bin/env python3 import os import sys import argparse import re import shutil import subprocess import shlex import copy import jsExtractFunctions as jsef; import ClassJsRef as ClassJsRef; # The main directory will be the top source directory: development mainDir = os.path.normpath(os.getcwd()); # print("mainDir:" + mainDir + "\n"); parser = argparse.ArgumentParser(description='''Script to extract JavaScript-related reference text from source code files.''') # parser.add_argument('-i', '--infiles', metavar='input_file', nargs='+', # required = False, help='Source files to process.') parser.add_argument('-t', '--tab', metavar='tabulator', required = False, help='String representing a tabulation key hit') parser.add_argument('-o', '--outfile', metavar='output file', nargs='?', required = False, help='File name to output the results to (or the console if not specified.') parser.add_argument('-l', '--listfile', metavar='file-list file', nargs='?', required = False, help='Name of the file that contains the list of files to process.') parser.add_argument('-d', '--dir', metavar='dir', nargs='?', required = False, help='Directory where the listing of the JS reference-containing files should occur.') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--extract', choices=['class', 'enum'], help='Extract reference for specific type. Provide a list of files to be processed with the --infiles argument.') group.add_argument('--list-files', choices=['class', 'enum'], help='List files having reference for specific type.') args = parser.parse_args() if args.tab: if args.tab == "\\t": tab = "\t" else: tab = args.tab else: #tab = "\n" tab = " " # if args.infiles: # print(f"Input files: {args.infiles}") # # if args.outfile: # print(f"Output file: {args.outfile}") # # if args.extract: # print(f"Extracting {args.extract}") # # if args.list: # print(f"Listing {args.list}") if args.listfile: with open(args.listfile, 'r') as f: content = f.read() # print(f"The contents of the file: {content}") args.infiles = content if args.list_files: if args.dir: root_dir = args.dir else: root_dir = os.getcwd() if args.list_files == "class": file_list = jsef.listAllJsRefFiles(root_dir, "BEGIN CLASS JS REFERENCE") elif args.list_files == "enum": file_list = jsef.listAllJsRefFiles(root_dir, "BEGIN ENUM JS REFERENCE") # print(f"The listed files found in {root_dir}:\n {file_list}") # Now generate a list of files that is injectable to this same program # as the --infiles parameter. all_files_in_a_string ="" for file in file_list: if file is not None: # print(f"{file} ") sys.stdout.write(file + ' ') #all_files_in_a_string = all_files_in_a_string + f" {file}" #print(all_files_in_a_string) if args.extract: if not args.infiles: print("Provide a list of header file paths to process or the name of a file that contains that list.", file = sys.stderr) exit(1) # This is a list of ClassJsRef instances. all_class_js_ref_results = [ ] if args.extract == "class": # args.infiles is a list #print(type(args.infiles)) file_list = args.infiles.split(' ') for file in file_list: # Now file is a string corresponding to a single file name. #print(type(file)) # print(f"Current file: {file}") if file is not None and len(file) > 0: # simplified_file = re.sub(r'\s$', '', file) # print(f"\nNow processing file '{file}'\n\n") # The returned object is a list of ClassJsRef instances # in case more than one class is JS reference documented. class_js_ref_results = jsef.extract_class_js_reference(file) # print(f"Classes parsed in {file}:\n") for class_js_ref in class_js_ref_results: print(f"{str(class_js_ref)}\n") # There might be nothing in there as Q_PROPERTY or Q_INVOKABLE stuff if not len(class_js_ref.properties) and not len(class_js_ref.invokables): # print("For id {results[0]}n there were no Q_PROPERTY or Q_INVOKABLE") pass else: all_class_js_ref_results.append(class_js_ref) # print("All the results:\n") # all_class_js_ref_results is a list of [ id, properties, invokables] # with id a tuple(name_space, class_name) # with properties a [ property_string ] # with invokables a [ invokable_string ] offset = 0 #print(f"Text{lead}four tabs") #print(f"Text{tab}one tabs") if args.outfile: # print(f"Now outputting the extracted JS reference text to:\n{args.outfile}\n") from pathlib import Path file_path = Path(args.outfile) file_handle = file_path.open('w', encoding='utf-8') for class_js_ref_result in all_class_js_ref_results: file_handle.write(class_js_ref_result.get_as_text(tab, offset)) else: # print(f"Now outputting the extracted JS reference text to terminal\n") for class_js_ref_result in all_class_js_ref_results: class_js_ref_result.print(tab, offset) libxpertmass-1.4.0/doc/javascript/scripts/jsExtractFunctions.py000664 001750 001750 00000035206 15100504560 026257 0ustar00rusconirusconi000000 000000 import os import sys import argparse import re import shutil import subprocess import shlex import copy import itertools from pathlib import Path import ClassJsRef as ClassJsRef import QinvokableJsRef as QinvokableJsRef import QpropertyJsRef as QpropertyJsRef # Matches ... namespace: ... line name_space_regex_string = r"^\s*\*?\s*namespace\s*:\s*([^\s.]+)\s*$" name_space_regex = re.compile(name_space_regex_string) # Matches ... class name: ... line class_name_regex_string = r"^\s*\*?\s*class name\s*:\s*([^\s.]+)\s*$" class_name_regex = re.compile(class_name_regex_string) # Matches property type and property name property_regex_string = r"^\s*Q_PROPERTY\(\s*([^\s.]+)\s+(\*?\s*[^\s?]+).*$" property_regex = re.compile(property_regex_string) # Matches everything after Q_INVOKABLE invokable_regex_string = r"^\s*Q_INVOKABLE\s*(.*)$" invokable_regex = re.compile(invokable_regex_string) def parse_q_property_block(opening_line, file_handle): # opening_line can start these two ways: # 1. /*$ JS PROP REF The .... .... */ # 2. Q_PROPERTY(Type variable WRITE write READ read) q_property_js_ref = QpropertyJsRef.QpropertyJsRef() ref_line_found = False js_ref_lines = [ ] statement_lines = [ ] opening_line = re.sub(r'^\s*', '', opening_line) opening_line = re.sub(r'\s*\n$', '', opening_line) if "$ JS PROP REF" in opening_line: # print(f"JS PROP REF: {opening_line}") ref_line_found = True js_ref_lines.append(opening_line) if "*/" not in opening_line: # print("*/ not in opening_line, multiline text") while(True): line = file_handle.readline() line = line.replace('\n', '') # In multiline, there will be chars at the beginning # and maybe at the end. Remove them because we need # to join the lines laters with ' ' and we do not want # multiple spaces in the finished text line. line = re.sub(r'^\s*', '', line) line = re.sub(r'\s*$', '', line) # print(f"Read follow-up JS PROP REF line '{line}'") js_ref_lines.append(line) if "*/" in line: # We have finished the JS REF block for the property to come. break; # Join the lines into a single string js_ref_as_single_line = " ".join(js_ref_lines) # Now purify: js_ref_as_single_line = re.sub(r'^\s*/\*\s*\$\s*JS PROP REF\s*', '', js_ref_as_single_line) js_ref_as_single_line = re.sub(r'\s*\*/', '', js_ref_as_single_line) # print(f"as a single line: {js_ref_as_single_line}") q_property_js_ref.description.append(js_ref_as_single_line) # print(f"After purification of property js ref: {q_property_js_ref.get_description_as_text(" ", 0)}") # At this point we need to know if we had a JS REF line or not if ref_line_found: # We need to read a line opening_line = file_handle.readline() if "Q_PROPERTY" not in opening_line: print("Failed to parse JS REF / Q_PROPERTY line", file=sys.stderr) sys.exit(1) statement_lines = [ opening_line.replace('\n', '') ]; # If the first line of the Q_PROPERTY statement # does not end by ')', then we are dealing with a # multiline block. if ')' not in opening_line: # print("Character ')' is not in the opening_line, this is a multi-line Q_PROPERTY statement") while(True): line = file_handle.readline() line = line.replace('\n', '') # print(f"Read follow-up Q_PROPERTY line {line}") statement_lines.append(line) if ')' in line: # print("Character ')' is in the follow-up line") # We are done. Exit the loop. break; else: pass #print("')' was found in the opening line"); # At this point we finally encountered the ')', either # because it was on the first block opening line or # because we iterated in next lines in the file. # So now make a single text string out of the string list statement_as_single_line = " ".join(statement_lines) #print(f"Joined: {statement_as_single_line}") simplified_statement_single_line = re.sub(r'\s+', ' ', statement_as_single_line) #print(f"Simplified {simplified_statement_single_line}") match = property_regex.match(simplified_statement_single_line); if(match): # print(f"Matched Q_PROPERTY: {match.group(1)} {match.group(2)}") q_property_js_ref.statement = f"{match.group(1)} {match.group(2)}" # print(f"Returning QpropertyJsRef {str(q_property_js_ref)}") return q_property_js_ref else: print("Failed to extract Q_PROPERTY statement.", file=sys.stderr) sys.exit(1) def parse_q_invokable_block(opening_line, file_handle): # opening_line can start these two ways: # 1. /*$ JS INVOK REF The .... .... */ # 2. Q_INVOKABLE explicit Utils(QObject *parent = nullptr); q_invokable_js_ref = QinvokableJsRef.QinvokableJsRef() ref_line_found = False js_ref_lines = [ ] invokable_lines = [ ] opening_line = re.sub(r'^\s*', '', opening_line) opening_line = re.sub(r'\s*\n$', '', opening_line) if "$ JS INVOK REF" in opening_line: # print(f"JS INVOK REF: {opening_line}") ref_line_found = True js_ref_lines.append(opening_line) if "*/" not in opening_line: # print("*/ not in opening_line, multiline text") while(True): line = file_handle.readline() line = line.replace('\n', '') # In multiline, there will be chars at the beginning # and maybe at the end. Remove them because we need # to join the lines laters with ' ' and we do not want # multiple spaces in the finished text line. line = re.sub(r'^\s*', '', line) line = re.sub(r'\s*$', '', line) # print(f"Read follow-up JS INVOK REF line {line}") js_ref_lines.append(line) if "*/" in line: # We have finished the JS REF block for the invokable to come. break; # Join the lines into a single string js_ref_as_single_line = " ".join(js_ref_lines) # Now purify: js_ref_as_single_line = re.sub(r'^\s*/\*\s*\$\s*JS INVOK REF\s*', '', js_ref_as_single_line) js_ref_as_single_line = re.sub(r'\s*\*/', '', js_ref_as_single_line) # print(f"After purification of invokable js ref: {js_ref_as_single_line}") q_invokable_js_ref.description.append(js_ref_as_single_line) # At this point we need to know if we had a JS REF line or not if ref_line_found: # We need to read a line opening_line = file_handle.readline() if "Q_INVOKABLE" not in opening_line: print("Failed to parse JS REF / Q_INVOKABLE line", file=sys.stderr) sys.exit(1) statement_lines = [ opening_line.replace('\n', '') ]; # If the first line of the Q_INVOKABLE statement # does not end by ';', then we are dealing with a # multiline block. if ';' not in opening_line: # print("Character ';' is not in the opening_line, this is a multi-line Q_INVOKABLE statement") while(True): line = file_handle.readline() line = line.replace('\n', '') statement_lines.append(line) if ';' in line: # print("Character ';' is in the follow-up line") # We are done. Exit the loop. break; else: # print("';' was found in the opening line"); pass # At this point we finally encountered the ';', either # because it was on the first block opening line or # because we iterated in next lines in the file. # So now make a single text string out of the string list statement_as_single_line = " ".join(statement_lines) # print(f"Joined: {statement_as_single_line}") simplified_statement_single_line = re.sub(r'\s+', ' ', statement_as_single_line) # print(f"Simplified {simplified_statement_single_line}") match = invokable_regex.match(simplified_statement_single_line); if(match): # print(f"Matched Q_INVOKABLE: '{match.group(1)}'") q_invokable_js_ref.statement = match.group(1) # print(f"Returning QinvokableJsRef with {q_invokable_js_ref.statement}") return q_invokable_js_ref else: print("Failed to extract Q_INVOKABLE statement.", file=sys.stderr) sys.exit(1) def parse_description_block(opening_line, file_handle): # print(f"Opening line for BEGIN DESCRIPTION") description_block_list = [ ]; # Store all the lines between this opening line and # the line that reads "END DESCRIPTION" # Pattern matches lines that start with optional whitespace followed by * # and some more optional spaces. We want to keep all the spaces to # maintain proper formatting. remove_star_and_spaces_in_start_pattern = r'^(\s*\*\s*)' while(True): line = file_handle.readline() if not "END DESCRIPTION" in line: new_line = re.sub(remove_star_and_spaces_in_start_pattern, r'', line, flags=re.MULTILINE) # print(f"Adding new description block line: '{new_line}'") description_block_list.append(new_line) else: break # return ("").join(description_block_list) return description_block_list def extract_class_js_reference(header_path): """Returns [ id, properties, invokables] with id = (name_space, class_name)""" # print(f"header_path: {header_path}"); if not os.path.exists(header_path): print("File " + header_path + " was not found\n", file = sys.stderr); return; # We want to iterate in the file in search for the JS markup of classes # that is in the form right before the class declaration: # # /* BEGIN JS REFERENCE # namespace: MsXpS::libXpertMassCore # class name: Polymer # */ # # and # /* END JS REFERENCE # namespace: MsXpS::libXpertMassCore # class name: Polymer # */ # right after the class declaration # Note that we may have more than one class # with JS reference text, so we will return # the results as a list. parsed_class_js_ref_texts = [ ] js_ref_block_opened = False; # Closed when first ';' is encountered. invokable_block_opened = False; # full_js_ref = [ id , properties, invokables ]; full_js_ref = [ ]; with open(header_path) as fileHandle: line = fileHandle.readline() while line: if re.match(r'^\s*//', line): line = fileHandle.readline() # print(f"New line is: {line}"); if "BEGIN CLASS JS REFERENCE" in line: # We are starting a JS reference block # Instantiate a class to be used as the # container for all the data. class_js_ref = ClassJsRef.ClassJsRef() js_ref_block_opened = True; line = fileHandle.readline() # print(f"Parsed line inside BEGIN CLASS JS REFERENCE: {line}") # Check if namespace: ... line match = name_space_regex.match(line); if match: if not js_ref_block_opened: print("Cannot have name space if JS ref block not opened", file=sys.stderr) sys.exit(1) name_space = match.group(1); else: print("Failed to parse the namespace", file=sys.stderr) sys.exit(1) line = fileHandle.readline() # print(f"Parsed line inside BEGIN CLASS JS REFERENCE: {line}") # Check if class name: ... line match = class_name_regex.match(line); if match: if not name_space: print("Cannot have class name if no name space.", file=sys.stderr) sys.exit(1) class_name = match.group(1); class_js_ref.name_space = name_space class_js_ref.class_name = class_name # print(f"Could set the name space and class name: {class_js_ref.name_space} - {class_js_ref.class_name}") else: print("Failed to parse the class name", file=sys.stderr) sys.exit(1) if "BEGIN DESCRIPTION" in line: class_js_ref.description = parse_description_block(line, fileHandle) if(len(class_js_ref.description)): # print(f"Set description for class {class_js_ref.class_name} is:\n {class_js_ref.description}") pass if "JS PROP REF" in line or "Q_PROPERTY" in line: # print("Detected line with JS PROP REF") if not len(class_js_ref.name_space) or not len(class_js_ref.class_name): print("Cannot be that a Q_PROPERTY block is opened while there is no name space nor class name.", file=sys.stderr); sys.exit(1) q_property_js_ref = parse_q_property_block(line, fileHandle) q_property_js_ref.name_space = class_js_ref.name_space q_property_js_ref.class_name = class_js_ref.class_name class_js_ref.properties.append(q_property_js_ref) # print(f"Appended new Q_PROPERTY: {q_property_js_ref.statement}") if "JS INVOK REF" in line or "Q_INVOKABLE" in line: # print("Detected line with JS INVOK REF") if not len(class_js_ref.name_space) or not len(class_js_ref.class_name): print("Cannot be that a Q_INVOKABLE block is opened while there is no name space nor class name.", file=sys.stderr); sys.exit(1) q_invokable_js_ref = parse_q_invokable_block(line, fileHandle) q_invokable_js_ref.name_space = class_js_ref.name_space q_invokable_js_ref.class_name = class_js_ref.class_name class_js_ref.invokables.append(q_invokable_js_ref) # print(f"Appended new Q_INVOKABLE: {q_invokable_js_ref.statement}") if "END CLASS JS REFERENCE" in line: if not js_ref_block_opened: print("Cannot be that JS ref. block is being closed while it was never opened.", file=sys.stderr) sys.exit(1) if not len(class_js_ref.name_space) or not len(class_js_ref.class_name): print("Cannot be that a Q_PROPERTY block is opened while there is no name space nor class name.", file=sys.stderr); sys.exit(1) line = fileHandle.readline() # print(f"line: {line}") # Make sure we are closing the right class JS reference block match = name_space_regex.match(line); if match: end_name_space = match.group(1); if end_name_space != name_space: print("Cannot be that a JS ref. block is closed with another name space.", file=sys.stderr) sys.exit(1) else: print("The class JS reference block fails to close correctly.", file=sys.stderr) sys.exit(1) line = fileHandle.readline() # print(f"line: {line}") match = class_name_regex.match(line); if match: end_class_name = match.group(1); if end_class_name != class_name: print("Cannot be that a JS ref. block is closed with another class name.", file=sys.stderr) sys.exit(1) js_ref_block_opened = False # So we've done parsing one class JS reference text, add the instantiated # class to the returned list. parsed_class_js_ref_texts.append(class_js_ref) # Go on with potential new class with JS reference text. line = fileHandle.readline() # print(f"Finished parsing {header_path}\n") return parsed_class_js_ref_texts def listAllJsRefFiles(root_dir, js_ref_bock_tag): root_path = Path(root_dir) matching_files = [] # Recursively search for both .hpp and .h files hpp_files = root_path.rglob("*.hpp") h_files = root_path.rglob("*.h") # Combine both iterators all_header_files = itertools.chain(hpp_files, h_files) for header_file in all_header_files: try: with open(header_file, 'r', encoding='utf-8') as f: content = f.read() if js_ref_bock_tag in content: matching_files.append(str(header_file)) except (UnicodeDecodeError, PermissionError, OSError) as e: print(f"Could not read {header_file}: {e}", file=sys.stderr) return matching_files libxpertmass-1.4.0/images/000775 001750 001750 00000000000 15100507137 016705 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/images/svg/000775 001750 001750 00000000000 15100507137 017504 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/images/svg/add-isotope.svg000664 001750 001750 00000004473 15100504560 022442 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/images/svg/remove-chemical-element.svg000664 001750 001750 00000004510 15100504560 024711 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/images/svg/remove-isotope.svg000664 001750 001750 00000004413 15100504560 023201 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/source/000775 001750 001750 00000000000 15100507137 016740 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/CMakeLists.txt000664 001750 001750 00000001610 15100504560 021473 0ustar00rusconirusconi000000 000000 configure_file(${CMAKE_UTILS_PATH}/src-config.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) # Now we can configure the build of both libraries add_subdirectory(XpertMassCore) add_subdirectory(XpertMassGui) # Generate and install config files include(CMakePackageConfigHelpers) if(UNIX) configure_package_config_file(${CMAKE_UTILS_PATH}/XpertMassConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/XpertMassConfig.cmake" INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/XpertMassConfigVersion.cmake" VERSION "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}" COMPATIBILITY SameMinorVersion) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/XpertMassConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/XpertMassConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass) endif() libxpertmass-1.4.0/source/XpertMassCore/000775 001750 001750 00000000000 15100507137 021477 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassCore/CMakeLists.txt000664 001750 001750 00000021047 15100504560 024240 0ustar00rusconirusconi000000 000000 #set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) #set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) message("\n${BoldGreen}Now configuring library libXpertMassCore${ColourReset}\n") # ############################################################################## # Files set(XpertMassCore_SRCS # # Most general features src/globals.cpp src/Utils.cpp src/Prop.cpp src/PropListHolder.cpp # # The basic chemical features src/Isotope.cpp src/IsotopicData.cpp src/IsotopicDataBaseHandler.cpp src/IsotopicDataLibraryHandler.cpp src/IsotopicDataUserConfigHandler.cpp src/IsotopicDataManualConfigHandler.cpp # src/ChemicalGroupRule.cpp src/ChemicalGroup.cpp src/PkaPhPi.cpp src/PkaPhPiDataParser.cpp # src/Formula.cpp src/Ionizer.cpp src/Modif.cpp src/Monomer.cpp src/MonomerDictionary.cpp # src/CrossLinker.cpp src/CrossLink.cpp src/CrossLinkedRegion.cpp # src/MassCollection.cpp # # Calculation engine configuration src/CalcOptions.cpp # # The mass peak shaping feature src/MassPeakShaper.cpp src/MassPeakShaperConfig.cpp src/IsotopicClusterShaper.cpp src/IsotopicClusterGenerator.cpp # # The chemical reactions src/CleavageMotif.cpp src/CleavageRule.cpp src/CleavageAgent.cpp src/CleavageConfig.cpp src/Cleaver.cpp src/FragmentationRule.cpp src/FragmentationConfig.cpp src/FragmentationPathway.cpp src/Fragmenter.cpp # # Polymer chemistry definition src/PolChemDefSpec.cpp src/PolChemDef.cpp # # Sequence entities src/IndexRange.cpp src/IndexRangeCollection.cpp src/Sequence.cpp src/Polymer.cpp src/Oligomer.cpp src/OligomerPair.cpp src/OligomerCollection.cpp # # The network functionality src/MassDataCborBaseHandler.cpp src/MassDataCborMassSpectrumHandler.cpp src/MassDataServer.cpp src/MassDataClient.cpp # # Tolerance src/Tolerance.cpp # # Scripting-related src/XpertMassCoreJavaScript.cpp src/jsclassregistrar.cpp ) qt6_add_resources(XpertMassCore_QRC_CPP xpertmasscore_resources.qrc) # This is only a workaround to the fact that although we list *.hpp and *.cpp # files as source files for the add_target() command, and although we # have the Q_NAMESPACE in the Enums namespace, the moc_globals.cpp file is # not added to the image files linked into the library). # This has as a bad effect of having not the Enums staticMetaObject in the # library, which then translates into an undefined symbol linker error. qt6_wrap_cpp(Core_MOC_SOURCES includes/MsXpS/libXpertMassCore/globals.hpp) # Because the header files are in their own directory and not along the source # files, we need to have them listed explicitely for automoc to work properly # below. # Create a variable to hold the 'includes' directory *relative* to the current # CMakeLists.txt file. set(INCLUDES_DIR "${CMAKE_CURRENT_LIST_DIR}/includes") file(GLOB_RECURSE XpertMassCore_HEADERS ${INCLUDES_DIR}/*.h ${INCLUDES_DIR}/*.hpp) message(STATUS "Included the header files from ${INCLUDES_DIR}: \n\ ${XpertMassCore_HEADERS}" ) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) # Sure we need this ? add_library(compiler_flags INTERFACE) # target_compile_features(compiler_flags INTERFACE cxx_std_11) if(CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE) include(CodeCoverage) append_coverage_compiler_flags() endif() # ############################################################################## # Configuration of the binary to be built # ############################################################################## # Only now can we add the library, because we have defined the sources. # ############################################################################## # Build the static lib add_library( Core_static STATIC ${XpertMassCore_HEADERS} ${XpertMassCore_SRCS} ${Core_MOC_SOURCES} # See explanation above about globals.cpp Enums ${XpertMassCore_QRC_CPP} ${PLATFORM_SPECIFIC_SOURCES} ) set_target_properties( Core_static PROPERTIES # Set target properties for namespace EXPORT_NAME "Core_static" OUTPUT_NAME XpertMassCore LINK_FLAGS "-Wl,--whole-archive" ) target_link_libraries( Core_static PUBLIC PappsoMSpp::Core IsoSpec++::IsoSpec++ Qt6::Core Qt6::Svg Qt6::Xml Qt6::Network Qt6::Qml ) # The install interface that is ${prefix}/include. target_include_directories( Core_static PUBLIC # These include directories are for building of this lib. $ # These include directories are for users of this lib. $ ) get_target_property(Core_static_INCLUDES Core_static INCLUDE_DIRECTORIES) message(STATUS "Core_static_INCLUDES: ${Core_static_INCLUDES}") install( TARGETS Core_static EXPORT XpertMassCoreStaticTargets.cmake LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Export static targets install( EXPORT XpertMassCoreStaticTargets.cmake FILE XpertMassCoreStaticTargets.cmake NAMESPACE XpertMass:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass ) export( EXPORT XpertMassCoreStaticTargets.cmake FILE "${CMAKE_CURRENT_BINARY_DIR}/XpertMassCoreStaticTargets.cmake" NAMESPACE XpertMass:: ) # ############################################################################## # Build the shared lib add_library( Core SHARED ${XpertMassCore_HEADERS} ${XpertMassCore_SRCS} ${Core_MOC_SOURCES} # See explanation above about globals.cpp Enums ${XpertMassCore_QRC_CPP} ${PLATFORM_SPECIFIC_SOURCES} ) set_target_properties( Core PROPERTIES # Set target properties for namespace EXPORT_NAME "Core" # The actual shared library name: libXpertMass OUTPUT_NAME XpertMassCore VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} LINK_FLAGS "-Wl,--no-as-needed" POSITION_INDEPENDENT_CODE ON ) target_link_libraries( Core PUBLIC PappsoMSpp::Core IsoSpec++::IsoSpec++ Qt6::Core Qt6::Xml Qt6::Network Qt6::Qml ) # The INSTALL_INTERFACE that is ${prefix}/include target_include_directories( Core PUBLIC # These include directories are for building of this lib. $ # These include directories are for users of this lib. $ ) get_target_property(Core_INCLUDES Core INCLUDE_DIRECTORIES) message(STATUS "Core_INCLUDES: ${Core_INCLUDES}") target_compile_definitions(Core PRIVATE "EXPORT_LIB_SYMBOLS") # This is to avoid the "include_next(math.h) file not found" error. if(WIN32) set_target_properties(Core_static PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) set_target_properties(Core PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) endif() install( TARGETS Core EXPORT XpertMassCoreSharedTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Export shared targets install( EXPORT XpertMassCoreSharedTargets FILE XpertMassCoreSharedTargets.cmake NAMESPACE XpertMass:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass ) export( EXPORT XpertMassCoreSharedTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/XpertMassCoreSharedTargets.cmake" NAMESPACE XpertMass:: ) # ############################################################################## # Common installation configuration message("The directory containing the includes: ${CMAKE_CURRENT_LIST_DIR}/includes/MsXpS/libXpertMassCore") install( DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/includes/MsXpS/libXpertMassCore DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/MsXpS" ) install( FILES ${CMAKE_CURRENT_LIST_DIR}/includes/MsXpS/export-import-config.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/MsXpS" ) message("") message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}") message("") message(STATUS "${BoldGreen}Finished configuration of libXpertMass.${ColourReset}") message("") message("\n${BoldGreen}Done configuring library libXpertMassCore${ColourReset}\n") libxpertmass-1.4.0/source/XpertMassCore/includes/000775 001750 001750 00000000000 15100507137 023305 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/000775 001750 001750 00000000000 15100507137 024317 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/export-import-config.h000664 001750 001750 00000000661 15100504560 030564 0ustar00rusconirusconi000000 000000 #pragma once #include namespace MsXpS { namespace libXpertMassCore { #if defined(EXPORT_LIB_SYMBOLS) // #pragma message("EXPORT_LIB_SYMBOLS is defined: defining DECLSPEC // Q_DECL_EXPORT") #define DECLSPEC Q_DECL_EXPORT #else // #pragma message("EXPORT_LIB_SYMBOLS is not defined: defining DECLSPEC // Q_DECL_IMPORT") #define DECLSPEC Q_DECL_IMPORT #endif } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/000775 001750 001750 00000000000 15100507137 027545 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CalcOptions.hpp000664 001750 001750 00000012755 15100504560 032503 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CalcOptions */ class DECLSPEC CalcOptions: public QObject { Q_OBJECT Q_PROPERTY( bool isDeepCalculation WRITE setDeepCalculation READ isDeepCalculation) Q_PROPERTY(Enums::MassType massType WRITE setMassType READ getMassType) Q_PROPERTY(Enums::CapType capType WRITE setCapType READ getCapType) Q_PROPERTY(Enums::ChemicalEntity monomerEntities WRITE setMonomerEntities READ getMonomerEntities) Q_PROPERTY(Enums::ChemicalEntity polymerEntities WRITE setPolymerEntities READ getPolymerEntities) Q_PROPERTY(Enums::CapType selectionType WRITE setCapType READ getCapType) public: Q_INVOKABLE explicit CalcOptions(QObject *parent = nullptr); Q_INVOKABLE explicit CalcOptions(bool deep_calculation, Enums::MassType mass_type, Enums::CapType selectionping, Enums::ChemicalEntity monomer_entities, Enums::ChemicalEntity polymer_entities, QObject *parent = nullptr); // Pseudo copy constructor Q_INVOKABLE explicit CalcOptions(const CalcOptions &other, QObject *parent = nullptr); Q_INVOKABLE CalcOptions &initialize(const CalcOptions &other); Q_INVOKABLE CalcOptions *clone(QObject *parent = nullptr); Q_INVOKABLE static CalcOptions *clone(const CalcOptions &other, QObject *parent = nullptr); virtual ~CalcOptions(); Q_INVOKABLE void setIndexRange(const IndexRange &sequence_range = IndexRange()); Q_INVOKABLE void setIndexRange(qsizetype index_start, qsizetype index_end); Q_INVOKABLE void setIndexRanges(const IndexRangeCollection &sequence_ranges); Q_INVOKABLE const IndexRangeCollection &getIndexRangeCollectionCstRef() const; Q_INVOKABLE IndexRangeCollection &getIndexRangeCollectionRef(); Q_INVOKABLE IndexRangeCollection *getIndexRangeCollection(); Q_INVOKABLE void setDeepCalculation(bool deep); Q_INVOKABLE bool isDeepCalculation() const; Q_INVOKABLE void setMassType(Enums::MassType mass_type); Q_INVOKABLE Enums::MassType getMassType() const; Q_INVOKABLE void setSelectionType(Enums::SelectionType selection_type); Q_INVOKABLE Enums::SelectionType getSelectionType() const; Q_INVOKABLE void setCapType(Enums::CapType selection_type); Q_INVOKABLE Enums::CapType getCapType() const; Q_INVOKABLE void setMonomerEntities(Enums::ChemicalEntity monomer_chem_ent); Q_INVOKABLE Enums::ChemicalEntity getMonomerEntities() const; Q_INVOKABLE void setPolymerEntities(Enums::ChemicalEntity polymer_chem_ent); Q_INVOKABLE Enums::ChemicalEntity getPolymerEntities() const; Q_INVOKABLE CalcOptions &operator=(const CalcOptions &other) = delete; Q_INVOKABLE bool operator==(const CalcOptions &other) const; Q_INVOKABLE bool operator!=(const CalcOptions &other) const; Q_INVOKABLE QString toString() const; static void registerJsConstructor(QJSEngine *engine); protected: // Tells if the calculation should involve the recalculation of // all the masses of the monomers of the sequence. bool m_deepCalculation = false; Enums::MassType m_massType = Enums::MassType::BOTH; Enums::CapType m_capType = Enums::CapType::BOTH; Enums::ChemicalEntity m_monomerEntities = Enums::ChemicalEntity::NONE; Enums::ChemicalEntity m_polymerEntities = Enums::ChemicalEntity::NONE; Enums::SelectionType m_selectionType = Enums::SelectionType::OLIGOMERS; IndexRangeCollection *mp_indexRangeCollection = nullptr; }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CalcOptions */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, CalcOptions) } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::CalcOptions); extern int calcOptionsMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/ChemicalGroup.hpp000664 001750 001750 00000007407 15100504560 033005 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/ChemicalGroupRule.hpp" #include "MsXpS/libXpertMassCore/Prop.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC ChemicalGroup { public: ChemicalGroup(const QString &name = QString(), float pka = 7.0, bool is_acid_charged = true, Enums::ChemicalGroupTrapped = Enums::ChemicalGroupTrapped::NEVER); ChemicalGroup(const ChemicalGroup &other); ~ChemicalGroup(); ChemicalGroup &operator=(const ChemicalGroup &other); void setName(QString); QString getName() const; void setPka(float); float getPka() const; void setAcidCharged(bool); bool isAcidCharged() const; void setPolRule(Enums::ChemicalGroupTrapped); Enums::ChemicalGroupTrapped getPolRule() const; const std::vector &getRulesCstRef() const; std::vector &getRulesRef(); ChemicalGroupRuleSPtr findRuleByEntity(const QString &entity, std::size_t &index) const; ChemicalGroupRuleSPtr findRuleByName(const QString &name, std::size_t &index) const; ChemicalGroupRuleSPtr findRuleByEntityAndName(const QString &entity, const QString &name, std::size_t &index) const; bool renderXmlMnmElement(const QDomElement &element); bool renderXmlMdfElement(const QDomElement &element); bool validate(ErrorList *error_list_p) const; bool isValid() const; protected: QString m_name; float m_pka; bool m_acidCharged; Enums::ChemicalGroupTrapped m_polymerizationRule; std::vector m_rules; mutable bool m_isValid = false; }; class DECLSPEC ChemicalGroupProp : public Prop { public: ChemicalGroupProp(const QString & = QString(), ChemicalGroup * = 0); ChemicalGroupProp(const ChemicalGroupProp &other); ~ChemicalGroupProp(); virtual void deleteData(); using Prop::operator=; ChemicalGroupProp &operator=(const ChemicalGroupProp &other); ChemicalGroupProp *cloneOut() const; bool renderXmlElement(const QDomElement &element, int version = 1); QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/ChemicalGroupRule.hpp000664 001750 001750 00000004701 15100504560 033627 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC ChemicalGroupRule { public: ChemicalGroupRule(const QString &name = QString(), const QString &entity = QString(), Enums::ChemicalGroupFate fate = Enums::ChemicalGroupFate::LOST); ChemicalGroupRule(const ChemicalGroupRule &other); ~ChemicalGroupRule(); void setEntity(const QString &entity); QString getEntity(); void setName(const QString &name); QString getName(); void setFate(Enums::ChemicalGroupFate); Enums::ChemicalGroupFate getFate(); ChemicalGroupRule &operator = (const ChemicalGroupRule &other); bool validate(ErrorList *error_list_p) const; bool isValid() const; bool renderXmlElement(const QDomElement &element); protected: QString m_name; QString m_entity; Enums::ChemicalGroupFate m_chemicalGroupFate; mutable bool m_isValid = false; }; typedef std::shared_ptr ChemicalGroupRuleSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageAgent.hpp000664 001750 001750 00000012455 15100504560 032750 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/CleavageMotif.hpp" #include "MsXpS/libXpertMassCore/CleavageRule.hpp" /////////////////////// libXpertMassGui includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CleavageAgent */ class DECLSPEC CleavageAgent: public QObject { Q_OBJECT public: CleavageAgent(QObject *parent = nullptr); CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version, QObject *parent = nullptr); CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp, const QString &name = QString(), const QString &pattern = QString(), QObject *parent = nullptr); // Pseudo copy-constructor that does not copy the QObject base class. CleavageAgent(const CleavageAgent &other, QObject *parent = nullptr); // Prevent accidental copying CleavageAgent(const CleavageAgent &other) = delete; ~CleavageAgent(); bool initialize(const CleavageAgent &other); //////////////// THE POLCHEMDEF ///////////////////// void setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolchemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); const QString &getName() const; //////////////// THE PATTERN ///////////////////// void setPattern(const QString &); const QString &getPattern() const; //////////////// THE MOTIFS ///////////////////// const std::vector &getMotifsCstRef() const; std::vector &getMotifsRef(); //////////////// THE RULES ///////////////////// const std::vector &getRulesCstRef() const; std::vector &getRulesRef(); CleavageRuleSPtr getCleavageRuleSPtrByName(const QString &name) const; //////////////// PARSING ///////////////////// bool parse(); //////////////// OPERATORS ///////////////////// // Prevent accidental copying CleavageAgent &operator=(const CleavageAgent &other) = delete; bool operator==(const CleavageAgent &other) const; bool operator!=(const CleavageAgent &other) const; //////////////// VALIDATIONS ///////////////////// CleavageAgentCstSPtr getFromPolChemDefByName() const; Enums::PolChemDefEntityStatus isKnownByNameInPolChemDef() const; CleavageRuleCstSPtr getCleavageRuleCstSPtrByName(const QString &name) const; int getCleavageRuleIndexByName(const QString &name) const; bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlClsElement(const QDomElement &element, int version = 1); bool renderXmlClaElement(const QDomElement &element, int version = 2); QString formatXmlClaElement(int offset, const QString &indent = Utils::xmlIndentationToken) const; //////////////// UTILS ///////////////////// QString toString() const; static void registerJsConstructor(QJSEngine *engine); protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; QString m_pattern = ""; std::vector m_motifs; std::vector m_rules; mutable bool m_isValid = false; }; typedef std::shared_ptr CleavageAgentSPtr; typedef std::shared_ptr CleavageAgentCstSPtr; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CleavageAgent */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, CleavageAgent) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageConfig.hpp000664 001750 001750 00000010356 15100504560 033115 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes /////////////////////// Qt includes /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/CleavageAgent.hpp" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CleavageConfig */ class DECLSPEC CleavageConfig: public CleavageAgent { Q_OBJECT Q_PROPERTY(int partials READ getPartials WRITE setPartials MEMBER m_partials) Q_PROPERTY(int startIonizeLevel READ getStartIonizeLevel WRITE setStartIonizeLevel MEMBER m_startIonizeLevel) Q_PROPERTY(int stopIonizeLevel READ getStopIonizeLevel WRITE setStopIonizeLevel MEMBER m_stopIonizeLevel) Q_PROPERTY(bool sequenceEmbedded READ isSequenceEmbedded WRITE setSequenceEmbedded MEMBER m_sequenceEmbedded) public: Q_INVOKABLE CleavageConfig(QObject *parent = nullptr); Q_INVOKABLE CleavageConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &pattern, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); Q_INVOKABLE CleavageConfig(const CleavageAgent &cleavage_agent, int partials = 0, bool is_sequence_embedded = false, QObject *parent = nullptr); // Pseudo copy constructor Q_INVOKABLE CleavageConfig(const CleavageConfig &other, QObject *parent = nullptr); ~CleavageConfig(); Q_INVOKABLE bool initialize(const CleavageConfig &other); Q_INVOKABLE bool setCleavageAgent(const CleavageAgent &cleavage_agent); void setPartials(int partials); int getPartials() const; void setStartIonizeLevel(int value); int getStartIonizeLevel() const; void setStopIonizeLevel(int value); int getStopIonizeLevel() const; Q_INVOKABLE void setIonizeLevels(int value1, int value2); void setSequenceEmbedded(bool is_sequence_embedded); bool isSequenceEmbedded() const; //////////////// OPERATORS ///////////////////// CleavageConfig &operator=(const CleavageConfig &other) = delete; bool operator==(const CleavageConfig &other) const; bool operator!=(const CleavageConfig &other) const; static void registerJsConstructor(QJSEngine *engine); protected: int m_partials = 0; // These two values have to be both positive and in increasing order // or equal. That is m_stopIonizeLevel >= m_startIonizeLevel. Only // use access functions to set their values. // Have to be 1 and not 0, otherwise the setting functions ensuring // that m_startIonizeLevel>=m_stopIonizeLevel will bug. int m_startIonizeLevel = 1; int m_stopIonizeLevel = 1; bool m_sequenceEmbedded =false; }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: CleavageConfig */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, CleavageConfig) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageMotif.hpp000664 001750 001750 00000006312 15100504560 032763 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC CleavageMotif { public: CleavageMotif( PolChemDefCstSPtr pol_chem_def_csp, const QString &motif = QString(), int offset = -1, Enums::CleavageAction cleavage_action = Enums::CleavageAction::NOT_SET); CleavageMotif(const CleavageMotif &other); ~CleavageMotif(); void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolChemDefCstSPtr() const; void setMotif(const QString &motif); QString getMotif() const; const std::vector &getMonomersCstRef() const; std::vector &getMonomersRef(); void setOffset(int offset); int getOffset() const; void setCleavageAction(Enums::CleavageAction cleavage_action); Enums::CleavageAction getCleavageAction() const; //////////////// OPERATORS ///////////////////// CleavageMotif &operator=(const CleavageMotif &other); bool operator==(const CleavageMotif &other) const; bool operator!=(const CleavageMotif &other) const; std::size_t parseSite(const QString &cleavage_site /*"Lys/Pro"*/); std::size_t parseMotif(const QString &motif /*"LysPro"*/); //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; std::vector m_monomers; std::size_t m_offset; Enums::CleavageAction m_cleavageAction = Enums::CleavageAction::NOT_SET; mutable bool m_isValid = false; }; typedef std::shared_ptr CleavageMotifSPtr; typedef std::shared_ptr CleavageMotifCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CleavageRule.hpp000664 001750 001750 00000007444 15100504560 032623 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC CleavageRule { public: CleavageRule(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &leftCode = QString(), const QString &leftFormula = QString(), const QString &rightCode = QString(), const QString &rightFormula = QString()); CleavageRule(const CleavageRule &other); ~CleavageRule(); //////////////// THE POLCHEMDEF ///////////////////// void setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolchemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); const QString &getName() const; //////////////// THE LEFT CODE ///////////////////// void setLeftCode(const QString &code); const QString &getLeftCode() const; //////////////// THE RIGHT CODE ///////////////////// void setRightCode(const QString &code); const QString &getRightCode() const; //////////////// THE LEFT FORMULA ///////////////////// void setLeftFormula(const Formula &formula); const Formula &getLeftFormula() const; //////////////// THE RIGHT FORMULA ///////////////////// void setRightFormula(const Formula &formula); const Formula &getRightFormula() const; //////////////// OPERATORS ///////////////////// CleavageRule &operator=(const CleavageRule &other); bool operator==(const CleavageRule &other) const; bool operator!=(const CleavageRule &other) const; //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlClrElement(const QDomElement &element, int version); QString formatXmlClrElement(int offset, const QString &indent = Utils::xmlIndentationToken); protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; QString m_leftCode = ""; Formula m_leftFormula; QString m_rightCode = ""; Formula m_rightFormula; mutable bool m_isValid = false; }; typedef std::shared_ptr CleavageRuleSPtr; typedef std::shared_ptr CleavageRuleCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Cleaver.hpp000664 001750 001750 00000006550 15100504560 031642 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Oligomer.hpp" #include "MsXpS/libXpertMassCore/CleavageAgent.hpp" #include "MsXpS/libXpertMassCore/CalcOptions.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" #include "MsXpS/libXpertMassCore/CleavageConfig.hpp" #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC Cleaver { public: Cleaver(); Cleaver(PolymerCstQSPtr polymer_cqsp, PolChemDefCstSPtr pol_chem_def_csp, const CleavageConfig &cleavage_config, const CalcOptions &calc_options, const Ionizer &ionizer); Cleaver(const Cleaver &other); ~Cleaver(); QString getCleaveAgentName() const; std::size_t transferOligomers(OligomerCollection &source, OligomerCollection &dest); const OligomerCollection &getOligomerCollectionCstRef() const; OligomerCollection &getOligomerCollectionRef(); bool cleave(bool reset = false); int cleavePartial(int partial_cleavage); int analyzeCrossLinks(OligomerCollection &oligomers); int analyzeCrossLinkedOligomer(OligomerSPtr oligomer_sp, OligomerCollection &oligomers); int fillCleavageIndices(); int resolveCleavageNoCleavage(); int removeDuplicateCleavageIndices(); int findCleavageMotif(CleavageMotif &cleavage_motif, size_t index_start, std::size_t index_stop); bool accountCleavageRule(CleavageRuleSPtr cleavage_rule_sp, OligomerSPtr oligomer_sp); //////////////// OPERATORS ///////////////////// Cleaver &operator=(const Cleaver &other); bool operator==(const Cleaver &other) const; bool operator!=(const Cleaver &other) const; protected: PolymerCstQSPtr mcsp_polymer; PolChemDefCstSPtr mcsp_polChemDef; CleavageConfig m_cleavageConfig; CalcOptions m_calcOptions; Ionizer m_ionizer; std::vector m_doCleaveIndices; std::vector m_doNotCleaveIndices; OligomerCollection m_oligomers; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CrossLink.hpp000664 001750 001750 00000016624 15100504560 032173 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/CrossLinker.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { class Polymer; typedef std::shared_ptr PolymerSPtr; typedef std::shared_ptr PolymerCstSPtr; typedef QSharedPointer PolymerQSPtr; typedef QSharedPointer PolymerCstQSPtr; class CrossLink; typedef CrossLink *CrossLinkRPtr; typedef const CrossLink *CrossLinkCstRPtr; typedef std::unique_ptr CrossLinkUPtr; typedef std::unique_ptr CrossLinkCstUPtr; typedef std::shared_ptr CrossLinkSPtr; typedef std::shared_ptr CrossLinkCstSPtr; typedef std::weak_ptr CrossLinkWPtr; typedef std::weak_ptr CrossLinkCstWPtr; using UuidCrossLinkCstWPtrPair = std::pair; using UuidCrossLinkWPtrPair = std::pair; class DECLSPEC CrossLink { public: CrossLink(); CrossLink(PolChemDefCstSPtr pol_chem_def_csp, PolymerCstQSPtr polymer_cqsp = nullptr, const QString &name = QString(), const QString &formula = QString(), const QString &comment = QString()); CrossLink(CrossLinkerCstSPtr cross_linker_csp, PolymerCstQSPtr polymer_cqsp = nullptr, const QString &comment = QString()); CrossLink(const CrossLink &other); ~CrossLink(); //////////////// THE POLYMER ///////////////////// PolymerCstQSPtr getPolymerCstSPtr() const; //////////////// THE CROSS-LINKER ///////////////////// const CrossLinkerCstSPtr getCrossLinkerCstSPtr() const; //////////////// THE COMMENT ///////////////////// void setComment(const QString &); const QString &getComment() const; //////////////// THE NAME ///////////////////// QString getCrossLinkerName() const; //////////////// THE MONOMERS ///////////////////// const std::vector &getMonomersCstRef() const; std::vector &getMonomersRef(); QString appendMonomer(MonomerCstSPtr monomer_csp); MonomerCstSPtr getMonomerAt(std::size_t index); bool removeMonomerAt(std::size_t index); std::size_t fillInMonomers(QString monomer_indices_text, bool &ok); std::vector continuumOfLocationsOfInclusiveSequenceMonomers( Enums::LocationType location_type) const; QString continuumOfLocationsOfInclusiveSequenceMonomersAsText( Enums::LocationType location_type) const; std::vector locationsOfOnlyExtremeSequenceMonomers(Enums::LocationType location_type) const; QString locationsOfOnlyExtremeSequenceMonomersAsText( Enums::LocationType location_type) const; std::size_t monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const; std::size_t monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const; MonomerCstSPtr getFirstMonomer() const; //////////////// OPERATORS ///////////////////// CrossLink &operator=(const CrossLink &other); bool operator==(const CrossLink &) const; bool operator!=(const CrossLink &) const; //////////////// CROSS-LINK LOGIC ///////////////////// Enums::CrossLinkEncompassed isEncompassedByIndexRange(std::size_t start, std::size_t end, std::size_t &in_count, std::size_t &out_count) const; Enums::CrossLinkEncompassed isEncompassedByIndexRangeCollection(const IndexRangeCollection &index_ranges, std::size_t &in_count, std::size_t &out_count) const; //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// MASS OPERATIONS ///////////////////// bool calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const; const CrossLink & accountMasses(double &mono, double &avg, int times = 1) const; double getMass(Enums::MassType mass_type) const; ////////////////UTILS ///////////////////// QString formatContainerOfMonomerLocationsAsText( const std::vector &locations) const; QString prepareResultsTxtString(); QString storeMonomer(const MonomerCstSPtr &monomer_csp); bool hasMonomer(const MonomerCstSPtr &monomer_csp) const; bool hasUuid(const MonomerCstSPtr &monomer_csp) const; MonomerCstSPtr getMonomerForUuid(const QString &uuid) const; QString getUuidForMonomer(const MonomerCstSPtr &monomer_csp) const; std::vector getAllMonomerUuids() const; QString toString() const; protected: CrossLinkerCstSPtr mcsp_crossLinker = nullptr; PolymerCstQSPtr mcsp_polymer = nullptr; QString m_comment; std::vector m_monomers; // We need in code that uses this class, to constantly keep at hand // the Monomer instances that are involved in CrossLink_s. For example, // we need to store the Monomer pointers as strings in QListWidget items // (Qt:UserRole). // We thus make use of the QUuid class to craft a Uuid string that // we associate to the Monomer weak pointer that in turn relates // to the Monomer shared pointer. // Note that the Monomer stored here are those in the Polymer.Sequence's // container of Monomer_s. The Polymer.Sequence's container of Monomer_s are // MonomerSPtr, but we want to have them stored here as CstSPtr, so that we // are sure not to modify them by error. std::vector m_uuidMonomerPairs; mutable bool m_isValid = false; private: bool removeMonomer(MonomerCstSPtr monomer_sp); void cleanupMonomers(); }; typedef std::shared_ptr CrossLinkSPtr; typedef std::shared_ptr CrossLinkCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::CrossLink); extern int crossLinkMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::CrossLink *); extern int crossLinkPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CrossLinkedRegion.hpp000664 001750 001750 00000005265 15100504560 033647 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CrossLink.hpp" namespace MsXpS { namespace libXpertMassCore { // [3] [4] [5] [6] [9] [11] // o---o---o---o---o--o---o---o---o---o---o---o---o---o---o // | |__| | | | | | // | +-----------+ +-------+ // | | // +------------------+ // // // In the example above, there are two cross-linked regions: [3--9] // and [11--13]. class DECLSPEC CrossLinkedRegion { public: CrossLinkedRegion(); CrossLinkedRegion(std::size_t index_start, std::size_t index_stop); CrossLinkedRegion(const CrossLinkedRegion &other); ~CrossLinkedRegion(); void setStartIndex(std::size_t index); std::size_t getStartIndex(); void setStopIndex(std::size_t index); std::size_t getStopIndex(); const std::vector &getCrossLinksCstRef() const; std::vector &getCrossLinksRef(); std::size_t appendCrossLink(const CrossLinkSPtr &cross_link_sp); std::size_t appendCrossLinks(const std::vector &cross_links); std::size_t removeCrossLink(CrossLinkSPtr &cross_link_sp); std::size_t removeCrossLinkAt(std::size_t index); CrossLinkedRegion &operator=(const CrossLinkedRegion &other); protected: std::size_t m_startIndex; std::size_t m_stopIndex; std::vector m_crossLinks; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/CrossLinker.hpp000664 001750 001750 00000012342 15100504560 032513 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" namespace MsXpS { namespace libXpertMassCore { typedef CrossLinker *CrossLinkerRPtr; typedef const CrossLinker *CrossLinkerCstRPtr; typedef std::unique_ptr CrossLinkerUPtr; typedef std::unique_ptr CrossLinkerCstUPtr; typedef std::shared_ptr CrossLinkerSPtr; typedef std::shared_ptr CrossLinkerCstSPtr; typedef std::weak_ptr CrossLinkerWPtr; typedef std::weak_ptr CrossLinkerCstWPtr; class DECLSPEC CrossLinker { public: CrossLinker(PolChemDefCstSPtr pol_chem_def_csp); CrossLinker(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version = 1); CrossLinker(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &formula = QString(), double mono = 0.0, double avg = 0.0); CrossLinker(const CrossLinker &other); virtual ~CrossLinker(); bool initialize(const QDomElement &element, int version = 1); //////////////// THE POLCHEMDEF ///////////////////// void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); const PolChemDefCstSPtr &getPolChemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); QString getName() const; //////////////// THE FORMULA ///////////////////// void setFormula(const QString &formula_string); const QString &getFormula() const; //////////////// THE MODIFICATIONS ///////////////////// const std::vector &getModifsCstRef() const; std::vector &getModifsRef(); bool insertModifAt(ModifCstSPtr modif_csp, std::size_t); bool appendModif(ModifCstSPtr modif_csp); ModifCstSPtr getModifAt(std::size_t index) const; bool removeModifAt(std::size_t index); bool hasModif(const QString &name); int modifIndex(const QString &name); //////////////// OPERATORS ///////////////////// CrossLinker &operator=(const CrossLinker &other); bool operator==(const CrossLinker &) const; bool operator!=(const CrossLinker &) const; //////////////// VALIDATIONS ///////////////////// const CrossLinkerCstSPtr getFromPolChemDefByName() const; Enums::PolChemDefEntityStatus isKnownByNameInPolChemDef() const; bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// MASS OPERATIONS ///////////////////// bool calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const; bool calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp); const CrossLinker &accountMasses(double *mono_p = nullptr, double *avg_p = nullptr, int times = 1) const; const CrossLinker & accountMasses(double &mono, double &avg, int times = 1) const; double getMass(Enums::MassType mass_type) const; //////////////// XML ///////////////////// bool renderXmlClkElement(const QDomElement &element, int version); QString formatXmlClkElement(int offset, const QString &indent = Utils::xmlIndentationToken) const; //////////////// UTILS ///////////////////// QString toString() const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; QString m_formula = ""; double m_mono = 0.0; double m_avg = 0.0; mutable bool m_isValid = false; // Modif instances might belong to the PolChemDef, but not necessarily. std::vector m_modifs; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Formula.hpp000664 001750 001750 00000024134 15100504560 031664 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes // For MSXPS_REGISTER_JS_CLASS(NS_IDENT, CLASS_NAME) // At the end of the file. #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "Isotope.hpp" #include "IsotopicData.hpp" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Formula */ class DECLSPEC Formula: public QObject { Q_OBJECT Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(QString actionFormula READ getActionFormula WRITE setActionFormula NOTIFY actionFormulaChanged) Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) public: enum class SplitResult : uint8_t { NOT_SET = 0x0000, FAILURE = 1 << 0, //!< The splitting work failed. HAS_PLUS_COMPONENT = 1 << 1, //!< The action formula a plus component HAS_MINUS_COMPONENT = 1 << 2, //!< The action formula a minus component HAS_BOTH_COMPONENTS = (HAS_PLUS_COMPONENT | HAS_MINUS_COMPONENT) //!< The action formula had both component types }; Q_ENUM(SplitResult) Q_INVOKABLE explicit Formula(QObject *parent = nullptr); explicit Formula(const QDomElement &element, int version = 1, QObject *parent = nullptr); Q_INVOKABLE explicit Formula(const QString &formula_string, QObject *parent = nullptr); // Pseudo copy constructor explicit Formula(const Formula &other, QObject *parent = nullptr); virtual ~Formula(); Q_INVOKABLE Formula *clone(const Formula &other, QObject *parent = nullptr); Q_INVOKABLE Formula &initialize(const Formula &other); //////////////// THE ACTIONFORMULA ///////////////////// Q_INVOKABLE void setActionFormula(const QString &formula); Q_INVOKABLE void setActionFormula(const Formula &formula); Q_INVOKABLE bool appendActionFormula(const QString &formula); Q_INVOKABLE QString getActionFormula(bool withTitle = false) const; //////////////// THE TITLE ///////////////////// Q_INVOKABLE void setTitle(const QString &title); Q_INVOKABLE QString getTitle() const; Q_INVOKABLE QString extractTitle() const; Q_INVOKABLE QString removeTitle(); //////////////// THE ATOM INDEX LOGIC ///////////////////// Q_INVOKABLE void setForceCountIndex(bool forceCountIndex); Q_INVOKABLE bool isForceCountIndex() const; //////////////// THE SYNTAX CHECKING LOGIC ///////////////////// Q_INVOKABLE static bool checkSyntax(const QString &formula, bool forceCountIndex = false); Q_INVOKABLE bool checkSyntax() const; //////////////// OPERATORS ///////////////////// virtual Formula &operator=(const Formula &other); virtual bool operator==(const Formula &other) const; virtual bool operator!=(const Formula &other) const; //////////////// THE SUB-FORMULAS OPERATIONS ///////////////////// SplitResult splitActionParts(IsotopicDataCstSPtr isotopic_data_csp, QString &plus_formula, QString &minus_formula, std::map &symbol_count_map, double times = 1, bool store = false, bool reset = false) const; SplitResult splitActionParts(IsotopicDataCstSPtr isotopic_data_csp, double times = 1, bool store = false, bool reset = false); Q_INVOKABLE static QChar actions(const QString &formula); Q_INVOKABLE QChar actions() const; Q_INVOKABLE bool hasNetMinusPart(); Q_INVOKABLE QString getPlusFormula() const; Q_INVOKABLE QString getMinusFormula() const; //////////////// VALIDATIONS ///////////////////// virtual bool validate(IsotopicDataCstSPtr isotopic_data_csp, ErrorList *error_list_p) const; virtual bool validate(IsotopicDataCstSPtr isotopic_data_csp, bool store, bool reset, ErrorList *error_list_p); virtual bool isValid() const; //////////////// THE SYMBOLS-COUNT OPERATIONS ///////////////////// const std::map &getSymbolCountMapCstRef() const; double symbolCount(const QString &symbol) const; bool accountSymbolCounts(IsotopicDataCstSPtr isotopic_data_csp, int times); double accountSymbolCountPair(std::map &symbol_count_map, const QString &symbol, double count = 1) const; double accountSymbolCountPair(const QString &symbol, double count = 1); std::size_t accountFormula(const QString &formula_string, IsotopicDataCstSPtr isotopic_data_csp, double times, bool &ok); //////////////// ELEMENTAL COMPOSITION ///////////////////// Q_INVOKABLE QString elementalComposition( std::vector> *symbol_count_pairs_p = nullptr) const; //////////////// MASS OPERATIONS ///////////////////// Formula &accountMasses(bool &ok, IsotopicDataCstSPtr isotopic_data_csp, double &mono, double &avg, double times = 1); static Formula &accountMasses(Formula &formula, bool &ok, IsotopicDataCstSPtr isotopic_data_csp, double &mono, double &avg, double times = 1); //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlFormulaElement(const QDomElement &element, int version = 1); Q_INVOKABLE QString formatXmlFormulaElement( int offset, const QString &indent = Utils::xmlIndentationToken); //////////////// UTILS ///////////////////// int removeSpaces(); double totalAtoms() const; double totalIsotopes(IsotopicDataCstSPtr isotopic_data_csp) const; void clear(); static void registerJsConstructor(QJSEngine *engine); signals: void titleChanged(); void actionFormulaChanged(); void validChanged(); protected: QString m_title; QString m_actionFormula; QString m_plusFormula; QString m_minusFormula; // Map that relates the chemical element symbols encounted in formula(e) to // their count in the formula(e). The count can be a double or an int. std::map m_symbolCountMap; // Tells if single atoms in a formula need nonetheless an index (H2O1, for // example) bool m_forceCountIndex = false; mutable bool m_isValid = false; //////////////// PRIVATE FUNCTIONS ///////////////////// //////////////// PRIVATE FUNCTIONS ///////////////////// //////////////// PRIVATE FUNCTIONS ///////////////////// private: void setPlusFormula(const QString &formula); void setMinusFormula(const QString &formula); }; // Overload bitwise OR operator inline Formula::SplitResult operator|(Formula::SplitResult lhs, Formula::SplitResult rhs) { return static_cast( static_cast>(lhs) | static_cast>(rhs)); } // Overload bitwise AND operator inline Formula::SplitResult operator&(Formula::SplitResult lhs, Formula::SplitResult rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } // Overload bitwise XOR operator inline Formula::SplitResult operator^(Formula::SplitResult lhs, Formula::SplitResult rhs) { return static_cast( static_cast>(lhs) ^ static_cast>(rhs)); } // Overload bitwise NOT operator inline Formula::SplitResult operator~(Formula::SplitResult opt) { return static_cast( ~static_cast>(opt)); } // Overload bitwise OR assignment operator inline Formula::SplitResult & operator|=(Formula::SplitResult &lhs, Formula::SplitResult rhs) { lhs = lhs | rhs; return lhs; } // Overload bitwise AND assignment operator inline Formula::SplitResult & operator&=(Formula::SplitResult &lhs, Formula::SplitResult rhs) { lhs = lhs & rhs; return lhs; } /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Formula */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Formula) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/FragmentationConfig.hpp000664 001750 001750 00000006676 15100504560 034216 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/FragmentationPathway.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC FragmentationConfig : public FragmentationPathway { public: FragmentationConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &formula, Enums::FragEnd frag_end = Enums::FragEnd::NE, const QString &comment = QString(), bool sequence_embedded = false); FragmentationConfig(const FragmentationPathway &fragmentationPathway, int start_index = 0, int stop_index = 0, bool sequence_embedded = false); FragmentationConfig(const FragmentationConfig &other); ~FragmentationConfig(); FragmentationConfig &operator=(const FragmentationConfig &other); void setStartIonizeLevel(std::size_t index); std::size_t getStartIonizeLevel() const; void setStopIonizeLevel(std::size_t index); std::size_t getStopIonizeLevel() const; void setIonizeLevels(std::size_t start, std::size_t stop); void setStartIndex(std::size_t value); std::size_t getStartIndex() const; void setStopIndex(std::size_t value); std::size_t getStopIndex() const; bool addFormula(const Formula &formula); bool addFormula(const QString &formula_string); const std::vector &getFormulasCstRef() const; std::vector &getFormulasRef(); void setSequenceEmbedded(bool); bool isSequenceEmbedded() const; QString toString() const; protected: std::size_t m_startIndex = 0; std::size_t m_stopIndex = 0; // These two values have to be both positive and in increasing order // or equal. That is m_stopIonizeLevel >= m_startIonizeLevel. Only // use access functions to set their values. // // Have to be 1 and not 0, otherwise the setting functions ensuring // that m_startIonizeLevel>=m_stopIonizeLevel will bug. std::size_t m_startIonizeLevel = 1; std::size_t m_stopIonizeLevel = 1; bool m_sequenceEmbedded; std::vector m_formulas; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/FragmentationPathway.hpp000664 001750 001750 00000012174 15100504560 034414 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/FragmentationRule.hpp" namespace MsXpS { namespace libXpertMassCore { class FragmentationPathway; typedef std::shared_ptr FragmentationPathwaySPtr; typedef std::shared_ptr FragmentationPathwayCstSPtr; class DECLSPEC FragmentationPathway { public: FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version); FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &formula_string = QString(), Enums::FragEnd frag_end = Enums::FragEnd::NE, const QString &comment = QString()); FragmentationPathway(const FragmentationPathway &other); virtual ~FragmentationPathway(); //////////////// THE POLCHEMDEF ///////////////////// void setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolchemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); const QString &getName() const; //////////////// THE FORMULA ///////////////////// void setFormula(const Formula &formula); void setFormula(const QString &formula_string); const Formula &getFormulaCstRef() const; Formula &getFormulaRef(); //////////////// THE FRAG END ///////////////////// void setFragEnd(Enums::FragEnd frag_end); Enums::FragEnd getFragEnd() const; //////////////// THE MONOMER CONTRIBUTION ///////////////////// void setMonomerContribution(int monomer_contribution); int getMonomerContribution() const; //////////////// THE COMMENT ///////////////////// void setComment(const QString &comment); QString getComment() const; //////////////// THE RULES CONTAINER ///////////////////// const std::vector &getRulesCstRef() const; std::vector &getRulesRef(); //////////////// OPERATORS ///////////////////// FragmentationPathway &operator=(const FragmentationPathway &other); bool operator==(const FragmentationPathway &other) const; bool operator!=(const FragmentationPathway &other) const; //////////////// RULES ///////////////////// void addRule(FragmentationRuleSPtr frag_rule_sp); void insertRuleAt(FragmentationRuleSPtr frag_rule_sp, std::size_t index); void removeRuleAt(size_t index); //////////////// VALIDATIONS ///////////////////// FragmentationPathwayCstSPtr getFromPolChemDefByName() const; Enums::PolChemDefEntityStatus isKnownByNameInPolChemDef() const; bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// UTILITIES ///////////////////// QString toString() const; //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlFgsElement(const QDomElement &element, int version = 1); bool renderXmlFgpElement(const QDomElement &element, int version = 2); QString formatXmlFgpElement(int offset, const QString &indent = Utils::xmlIndentationToken) const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; Formula m_formula; Enums::FragEnd m_fragEnd = Enums::FragEnd::NE; //! Fragmented monomer's mass contribution. See fragSpecDefDlg.cpp //! for a detailed explanation of what this member is for. int m_monomerContribution = 0; QString m_comment; std::vector m_rules; mutable bool m_isValid = false; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/FragmentationRule.hpp000664 001750 001750 00000010675 15100504560 033712 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC FragmentationRule { public: FragmentationRule(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &prev_code = QString(), const QString ¤t_code = QString(), const QString &next_code = QString(), const QString &comment = QString(), const QString &formula_string = QString()); FragmentationRule(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version); FragmentationRule(const FragmentationRule &other); virtual ~FragmentationRule(); //////////////// THE POLCHEMDEF ///////////////////// void setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolchemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); const QString &getName(); //////////////// THE PREVIOUS MONOMER CODE ///////////////////// void setPrevCode(const QString &code); const QString &getPrevCode() const; //////////////// THE CURRENT MONOMER CODE ///////////////////// void setCurrCode(const QString &code); const QString &getCurrCode() const; //////////////// THE NEXT MONOMER CODE ///////////////////// void setNextCode(const QString &code); const QString &getNextCode() const; //////////////// THE COMMENT ///////////////////// void setComment(const QString &comment); const QString &getComment() const; //////////////// THE FORMULA ///////////////////// void setFormula(const Formula &formula); void setFormula(const QString &formula_string); const Formula &getFormulaCstRef() const; Formula &getFormulaRef(); //////////////// OPERATORS ///////////////////// FragmentationRule &operator=(const FragmentationRule &other); bool operator==(const FragmentationRule &other) const; bool operator!=(const FragmentationRule &other) const; //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// UTILITIES ///////////////////// QString toString() const; //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlFgrElement(const QDomElement &element); QString formatXmlFgrElement(int offset, const QString &indent = Utils::xmlIndentationToken); protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name; QString m_prevCode; QString m_currCode; QString m_nextCode; QString m_comment; Formula m_formula; mutable bool m_isValid = false; }; typedef std::shared_ptr FragmentationRuleSPtr; typedef std::shared_ptr FragmentationRuleCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Fragmenter.hpp000664 001750 001750 00000007610 15100504560 032351 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/libXpertMassCore/FragmentationPathway.hpp" #include "MsXpS/libXpertMassCore/FragmentationConfig.hpp" #include "MsXpS/libXpertMassCore/CalcOptions.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" #include "MsXpS/libXpertMassCore/CrossLinkedRegion.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC Fragmenter { public: Fragmenter(PolymerCstQSPtr polymer_cqsp, PolChemDefCstSPtr pol_chem_def_csp, const std::vector &fragmentation_configs, const CalcOptions &calc_options, const Ionizer &ionizer); Fragmenter(const Fragmenter &other); ~Fragmenter(); void addFragmentationConfig(const FragmentationConfig &fragmentation_config); std::size_t transferOligomers(OligomerCollection &source, OligomerCollection &dest_oligomers); void transferOligomer(OligomerSPtr &&source_oligomer_sp, OligomerCollection &dest_oligomers); const OligomerCollection &getOligomerCollectionCstRef() const; OligomerCollection &getOligomerCollectionRef(); bool fragment(); int fragmentEndNone(const FragmentationConfig &fragmentation_config); int fragmentEndLeft(const FragmentationConfig &fragmentation_config); int fragmentEndRight(const FragmentationConfig &fragmentation_config); bool accountFragmentationRule(FragmentationRuleSPtr fragmentation_rule_sp, bool only_for_checking, std::size_t monomer_index, Enums::FragEnd frag_end, double &mono, double &avg); std::size_t accountFormulas(OligomerSPtr &&template_oligomer_sp, OligomerCollection &oligomers, const FragmentationConfig &fragmentation_config, const QString &name, int charge); OligomerCollection accountIonizationLevels(OligomerCollection &oligomers, const FragmentationConfig &fragmentation_config); protected: PolymerCstQSPtr mcsp_polymer; PolChemDefCstSPtr mcsp_polChemDef; std::vector m_fragmentationConfigs; CalcOptions m_calcOptions; Ionizer m_ionizer; OligomerCollection m_oligomers; // A container of CrossLinkedRegion instances that we compute in case // there are cross-links in the fragmented sequence. std::vector m_crossLinkedRegions; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IndexRange.hpp000664 001750 001750 00000006423 15100504560 032304 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IndexRange */ class DECLSPEC IndexRange: public QObject { Q_OBJECT Q_PROPERTY(qsizetype start MEMBER m_start) Q_PROPERTY(qsizetype stop MEMBER m_stop) friend class IndexRangeCollection; public: qsizetype m_start = std::numeric_limits::max(); qsizetype m_stop = std::numeric_limits::max(); Q_INVOKABLE explicit IndexRange(QObject *parent = nullptr); Q_INVOKABLE explicit IndexRange(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); // Pseudo copy constructor Q_INVOKABLE explicit IndexRange(const IndexRange &other, QObject *parent = nullptr); virtual ~IndexRange(); Q_INVOKABLE void initialize(const IndexRange &other); Q_INVOKABLE IndexRange *clone(QObject *parent = nullptr); Q_INVOKABLE static IndexRange *clone(const IndexRange &other, QObject *parent = nullptr); bool operator=(const IndexRange &other) = delete; Q_INVOKABLE bool operator==(const IndexRange &other) const; Q_INVOKABLE bool operator!=(const IndexRange &other) const; Q_INVOKABLE void sortAscending(); Q_INVOKABLE void sortDescending(); Q_INVOKABLE bool isValid() const; Q_INVOKABLE void reset(); QString indicesAsText() const; QString positionsAsText() const; static void registerJsConstructor(QJSEngine *engine); }; typedef QSharedPointer IndexRangeQSPtr; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IndexRange */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, IndexRange) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IndexRangeCollection.hpp000664 001750 001750 00000013503 15100504560 034315 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IndexRange.hpp" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IndexRangeCollection */ class DECLSPEC IndexRangeCollection: public QObject { Q_OBJECT Q_PROPERTY(QString comment WRITE setComment READ getComment) friend class Polymer; friend class Oligomer; public: Q_INVOKABLE explicit IndexRangeCollection(QObject *parent = nullptr); Q_INVOKABLE explicit IndexRangeCollection(qsizetype index_start, qsizetype index_stop, QObject *parent = nullptr); Q_INVOKABLE explicit IndexRangeCollection( const QString &index_ranges_string, Enums::LocationType location_type = Enums::LocationType::INDEX, QObject *parent = nullptr); // Pseudo copy constructor Q_INVOKABLE explicit IndexRangeCollection(const IndexRangeCollection &other, QObject *parent = nullptr); virtual ~IndexRangeCollection(); IndexRangeCollection &initialize(const IndexRangeCollection &other); IndexRangeCollection *clone(QObject *parent = nullptr); static IndexRangeCollection *clone(const IndexRangeCollection &other, QObject *parent = nullptr); void setComment(const QString &comment); QString getComment() const; Q_INVOKABLE const QList &getRangesCstRef() const; Q_INVOKABLE QList &getRangesRef(); Q_INVOKABLE static QList parseIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type, QObject *parent = nullptr); //////////////// OPERATORS ///////////////////// IndexRangeCollection &operator=(const IndexRangeCollection &other) = delete; Q_INVOKABLE bool operator==(const IndexRangeCollection &other) const; Q_INVOKABLE bool operator!=(const IndexRangeCollection &other) const; //////////////// SEQUENCE RANGES and COORDINATES ///////////////////// Q_INVOKABLE void setIndexRange(qsizetype start, qsizetype stop); Q_INVOKABLE void setIndexRange(const IndexRange &index_range); Q_INVOKABLE void setIndexRanges(const QList &index_ranges); Q_INVOKABLE void setIndexRanges(const IndexRangeCollection &index_ranges); Q_INVOKABLE qsizetype setIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type); Q_INVOKABLE void appendIndexRange(qsizetype start, qsizetype stop); Q_INVOKABLE void appendIndexRange(const IndexRange &index_range); Q_INVOKABLE void appendIndexRanges(const QList &index_ranges); Q_INVOKABLE void appendIndexRanges(const IndexRangeCollection &index_ranges); //////////////// ACCESSING FUNCTIONS ///////////////////// const IndexRange &getRangeCstRefAt(qsizetype index) const; IndexRange &getRangeRefAt(qsizetype index); IndexRange *getRangeAt(qsizetype index); const QList::const_iterator getRangeCstIteratorAt(qsizetype index) const; const QList::iterator getRangeIteratorAt(qsizetype index); Q_INVOKABLE qsizetype leftMostIndexRangeStart() const; Q_INVOKABLE QList indicesOfLeftMostIndexRanges() const; Q_INVOKABLE bool isLeftMostIndexRange(const IndexRange &index_range) const; Q_INVOKABLE qsizetype rightMostIndexRangeStop() const; Q_INVOKABLE QList indicesOfRightMostIndexRanges() const; Q_INVOKABLE bool isRightMostIndexRange(const IndexRange &index_range) const; Q_INVOKABLE IndexRange *mostInclusiveLeftRightIndexRange() const; Q_INVOKABLE bool encompassIndex(qsizetype index, bool globally = false) const; Q_INVOKABLE bool overlap() const; Q_INVOKABLE QString indicesAsText() const; Q_INVOKABLE QString positionsAsText() const; qsizetype size() const; void clear(); static void registerJsConstructor(QJSEngine *engine); protected: QList m_ranges; QString m_comment; }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IndexRangeCollection */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, IndexRangeCollection) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Ionizer.hpp000664 001750 001750 00000013324 15100504560 031675 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Ionizer */ class DECLSPEC Ionizer: public QObject { Q_OBJECT // Q_PROPERTY( // Formula formula READ getFormulaCstRef WRITE setFormula MEMBER m_formula) Q_PROPERTY(int nominalCharge READ getNominalCharge WRITE setNominalCharge MEMBER m_nominalCharge) Q_PROPERTY(int level READ getLevel WRITE setLevel MEMBER m_level) Q_PROPERTY(bool isValid READ isValid MEMBER m_isValid) // Q_PROPERTY(Formula currentStateFormula READ getCurrentStateFormulaCstRef // MEMBER m_currentStateFormula) Q_PROPERTY(int currentStateNominalCharge READ getCurrentStateNominalCharge MEMBER m_currentStateNominalCharge) Q_PROPERTY( int currentStateLevel READ getCurrentStateLevel MEMBER m_currentStateLevel) Q_PROPERTY(bool isCurrentStateValid READ isCurrentStateValid MEMBER m_isCurrentStateValid) // friend class Polymer; public: Q_INVOKABLE Ionizer(QObject *parent = nullptr); Ionizer(IsotopicDataCstSPtr isotopic_data_csp, QObject *parent = nullptr); Ionizer(IsotopicDataCstSPtr isotopic_data_csp, const Formula &formula, int charge, int level, QObject *parent = nullptr); Ionizer(IsotopicDataCstSPtr isotopic_data_csp, const QString &formula_string, int charge, int level, QObject *parent = nullptr); // Pseudo copy constructor Ionizer(const Ionizer &other, QObject *parent = nullptr); void initialize(const Ionizer &other); void setIsotopicDataCstSPtr(IsotopicDataCstSPtr isotopic_data_csp); IsotopicDataCstSPtr getIsotopicDataCstSPtr() const; void setFormula(const Formula &formula); void setFormula(const QString &formula_string); const Formula &getFormulaCstRef() const; Formula &getFormulaRef(); const Formula &getCurrentStateFormulaCstRef() const; void setNominalCharge(int nominal_charge); int getNominalCharge() const; int getCurrentStateNominalCharge() const; Q_INVOKABLE int charge() const; Q_INVOKABLE int currentStateCharge() const; Q_INVOKABLE void forceCurrentState(const Formula &formula, int nominal_charge, int level); Q_INVOKABLE void forceCurrentStateLevel(int level); void setLevel(int level); int getLevel() const; int getCurrentStateLevel() const; Q_INVOKABLE Ionizer makeIonizerWithCurrentStateData(); bool isIonized() const; Enums::IonizationOutcome ionize(double &mono, double &avg) const; Enums::IonizationOutcome deionize(double &mono, double &avg) const; Enums::IonizationOutcome molecularMasses(double &mono, double &avg) const; Ionizer &operator=(const Ionizer &other); bool operator==(const Ionizer &other) const; bool operator!=(const Ionizer &other) const; bool renderXmlIonizeRuleElement(const QDomElement &element); QString formatXmlIonizeRuleElement( int, const QString &indent = Utils::xmlIndentationToken); Q_INVOKABLE bool validate(ErrorList *error_list_p) const; bool isValid() const; Q_INVOKABLE bool validateCurrentState(ErrorList *error_list_p) const; bool isCurrentStateValid() const; //////////////// UTILS ///////////////////// QString toString(bool with_title = true) const; void clear(); static void registerJsConstructor(QJSEngine *engine); protected: IsotopicDataCstSPtr mcsp_isotopicData = nullptr; Formula m_formula; int m_nominalCharge = 0; int m_level = 0; mutable bool m_isValid = false; mutable Formula m_currentStateFormula; mutable int m_currentStateNominalCharge = 0; mutable int m_currentStateLevel = 0; mutable bool m_isCurrentStateValid = false; }; typedef std::shared_ptr IonizerSPtr; typedef std::shared_ptr IonizerCstSPtr; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Ionizer */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Ionizer) } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Ionizer); extern int ionizerMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Ionizer *); extern int ionizerPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Isotope.hpp000664 001750 001750 00000014616 15100504560 031705 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { // #include // // extern const int elem_table_atomicNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int elem_table_massNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int // elem_table_extraNeutrons[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const bool elem_table_Radioactive[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_log_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; enum class Version1IsotopeFields { ID = 0, ELEMENT = 1, SYMBOL = 2, ATOMIC_NUMBER = 3, MASS = 4, MASS_NUMBER = 5, EXTRA_NEUTRONS = 6, PROBABILITY = 7, LN_PROBABILITY = 8, RADIOACTIVE = 9, LAST = 10, }; enum class IsotopeFields { NAME = 0, SYMBOL = 1, MASS = 2, PROBABILITY = 3, LAST = 4, }; /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Isotope */ class DECLSPEC Isotope: public QObject { Q_OBJECT // We need the READ WRITE get/set functions because there is validation code. Q_PROPERTY(QString name READ getName WRITE setName) Q_PROPERTY(QString symbol READ getSymbol WRITE setSymbol) Q_PROPERTY(double mass READ getMass WRITE setMass) Q_PROPERTY(double probability READ getProbability WRITE setProbability) // No setter because it should be set by the validation function only. Q_PROPERTY(bool isValid READ isValid) public: enum class Version1IsotopeFields { ID = 0, ELEMENT = 1, SYMBOL = 2, ATOMIC_NUMBER = 3, MASS = 4, MASS_NUMBER = 5, EXTRA_NEUTRONS = 6, PROBABILITY = 7, LN_PROBABILITY = 8, RADIOACTIVE = 9, LAST = 10, }; Q_ENUM(Version1IsotopeFields) enum class IsotopeFields { NAME = 0, SYMBOL = 1, MASS = 2, PROBABILITY = 3, LAST = 4, }; Q_ENUM(IsotopeFields) Q_INVOKABLE Isotope(QObject *parent = nullptr); Q_INVOKABLE Isotope(const QString &name, const QString &symbol, double mass, double probability, QObject *parent = nullptr); Q_INVOKABLE Isotope(const QString &text, QObject *parent = nullptr); // Pseudo copy constructor Isotope(const Isotope &other, QObject *parent = nullptr); Isotope(const Isotope *other_p, QObject *parent = nullptr); virtual ~Isotope(); Q_INVOKABLE Isotope *clone(QObject *parent = nullptr) const; static Isotope *clone(const Isotope &other, QObject *parent = nullptr); Q_INVOKABLE bool initialize(const QString &name, const QString &symbol, double mass, double probability); Q_INVOKABLE bool initialize(const QString &text); bool initializeVersion1(const QString &text); bool initializeVersion2(const QString &text); Q_INVOKABLE void clear(); Q_INVOKABLE void setName(const QString &name); Q_INVOKABLE QString getName() const; Q_INVOKABLE void setSymbol(const QString &symbol); Q_INVOKABLE QString getSymbol() const; Q_INVOKABLE void setMass(double mass); Q_INVOKABLE double getMass() const; Q_INVOKABLE void setProbability(double probability); Q_INVOKABLE double getProbability() const; Q_INVOKABLE bool validate(ErrorList *error_list_p); bool isValid() const; bool operator=(const Isotope &other) const = delete; bool operator==(const Isotope &other) const; bool operator!=(const Isotope &other) const; Q_INVOKABLE QString toString() const; static void registerJsConstructor(QJSEngine *engine); protected: QString m_name = ""; QString m_symbol = ""; double m_mass = -1.0; double m_probability = -1.0; bool m_isValid = false; }; typedef QSharedPointer IsotopeQSPtr; typedef QSharedPointer IsotopeCstQSPtr; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Isotope */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Isotope) } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Isotope); extern int isotopeMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Isotope *); extern int isotopePtrMetaTypeId; Q_DECLARE_METATYPE(QSharedPointer); extern int isotopeSPtrMetaTypeId; Q_DECLARE_METATYPE(QSharedPointer); extern int isotopeCstSPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp000664 001750 001750 00000013002 15100504560 035251 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once #include #include #include /////////////////////// Qt includes /////////////////////// pappsomspp includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" namespace MsXpS { namespace libXpertMassCore { enum class IsotopicDataType { NOT_SET, LIBRARY_CONFIG, USER_CONFIG, MANUAL_CONFIG, }; using FormulaChargePair = std::pair; using IsotopicClusterChargePair = std::pair; // We feed pairs to the generator and it produces for each // pair an isotopic cluster stored as a pair. class DECLSPEC IsotopicClusterGenerator { public: IsotopicClusterGenerator(); IsotopicClusterGenerator(libXpertMassCore::IsotopicDataSPtr isotopic_data_sp); virtual ~IsotopicClusterGenerator(); void setIsotopicDataType(IsotopicDataType isotopic_data_type); void setIsotopicData(libXpertMassCore::IsotopicDataSPtr isotopic_data_sp); libXpertMassCore::IsotopicDataSPtr getIsotopicData() const; void setFormulaChargePairs( const std::vector &formula_charge_pairs); void setFormulaChargePair(FormulaChargePair &formula_charge_pair); void appendFormulaChargePair(FormulaChargePair &formula_charge_pair); void setMaxSummedProbability(double max_probability); void setNormalizationIntensity(int normalize_intensity); void setSortType(pappso::Enums::SortType sort_type); void setSortOrder(pappso::Enums::SortOrder sort_order); bool configureIsotopicData(std::map &symbol_count_map, int *&per_element_isotopes_count_array_p, int *&per_element_symbol_count_array_p, double **&per_element_isotope_masses_arrays_p_p, double **&per_element_isotope_probs_arrays_p_p); bool validateFormula(Formula &formula); bool validateAllFormulas(); pappso::TraceSPtr runIsotopicDataCalculations(std::size_t element_count, int charge, int *per_element_isotopes_count_array_p, int *per_element_symbol_count_array_p, double **per_element_isotope_masses_arrays_p_p, double **per_element_isotope_probs_arrays_p_p); IsotopicClusterChargePair generateIsotopicClusterCentroids(FormulaChargePair formula_charge_pair); std::size_t run(); QString clusterToString(const pappso::TraceCstSPtr &isotopic_cluster_sp) const; QString clustersToString() const; const std::vector & getIsotopicClusterChargePairs() const; protected: // Basic configuration libXpertMassCore::IsotopicDataSPtr msp_isotopicData = nullptr; IsotopicDataType m_isotopicDataType = IsotopicDataType::NOT_SET; double m_maxSummedProbability = 0.95; int m_normalizeIntensity = std::numeric_limits::min(); // Convenience for the useur pappso::Enums::SortType m_sortType = pappso::Enums::SortType::none; pappso::Enums::SortOrder m_sortOrder = pappso::Enums::SortOrder::ascending; // Reminder // using FormulaChargePair = std::pair; // using IsotopicClusterChargePair = std::pair; // The starting material: a formula string and a corresponding charge. std::vector m_formulaChargePairs; // The clusters that are generated are stored using the charge. std::vector m_isotopicClusterChargePairs; void normalizeIntensities(pappso::TraceSPtr &isotopic_cluster_sp); void sortPeakCentroids(pappso::TraceSPtr &isotopic_cluster_sp); void sortPeakCentroidsByMz(pappso::TraceSPtr &isotopic_cluster_sp); void sortPeakCentroidsByIntensity(pappso::TraceSPtr &isotopic_cluster_sp); }; typedef std::shared_ptr IsotopicClusterGeneratorSPtr; typedef std::shared_ptr IsotopicClusterGeneratorCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicClusterShaper.hpp000664 001750 001750 00000011772 15100504560 034561 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MassPeakShaperConfig.hpp" #include "MassPeakShaper.hpp" namespace MsXpS { namespace libXpertMassCore { using IsotopicClusterChargePair = std::pair; class DECLSPEC IsotopicClusterShaper { public: // Version where only one isotopic cluster (centroid data) is to be // processed. IsotopicClusterShaper(const pappso::Trace &isotopic_cluster, int charge, const libXpertMassCore::MassPeakShaperConfig &config); IsotopicClusterShaper( const std::vector &isotopic_cluster_charge_pairs, const libXpertMassCore::MassPeakShaperConfig &config); virtual ~IsotopicClusterShaper(); void setConfig(const MassPeakShaperConfig &config); const MassPeakShaperConfig *getConfig() const; // Single isotopic cluster version. Reset automatically the shaped isotopic // clusters. void setIsotopicCluster(const pappso::Trace &isotopic_cluster, int charge); void setIsotopicCluster(pappso::TraceCstSPtr isotopic_cluster_sp, int charge); void setIsotopicCluster(IsotopicClusterChargePair isotopic_cluster_charge_pair); // Multiple isotopic clusters version. Reset automatically the shaped isotopic // clusters. void setIsotopicClusterChargePairs(const std::vector &isotopic_cluster_charge_pairs); void appendIsotopicCluster(const pappso::Trace &isotopic_cluster, int charge); void appendIsotopicClusterChargePairs(const std::vector &isotopic_cluster_charge_pairs); // The normalizing intensity usually is the greatest intensity in the cluster // centroids that is to be used for normalization of the peaks in the cluster. void setNormalizeIntensity(int new_max_intensity); int getNormalizeIntensity() const; pappso::Trace &run(bool reset = true); QString shapeToString(); protected: // This is the starting material for the shaping. There can be as many // isotopic clusters (centroid data) in the vector as necessary. All these // clusters are going to be processed and the resulting shaped isotopic // cluster data are combined into the member MapTrace. std::vector m_isotopicClusterChargePairs; libXpertMassCore::MassPeakShaperConfig *mp_massPeakShaperConfig; pappso::MapTrace m_mapTrace; pappso::Trace m_finalTrace; pappso::MzIntegrationParams m_mzIntegrationParams; // Of all the peak centroids' intensities, what is the m/z value of the most // intense? double m_mostIntensePeakMz = 0.0; double m_smallestMz = std::numeric_limits::max(); double m_greatestMz = std::numeric_limits::min(); int m_normalizeIntensity = 1; void setIsotopicCluster(pappso::TraceCstSPtr isotopic_cluster_sp, int charge, bool reset); void setIsotopicCluster(const pappso::Trace &isotopic_cluster, int charge, bool reset); void setIsotopicCluster(IsotopicClusterChargePair isotopic_cluster_charge_pair, bool reset); void setIsotopicClusterChargePairs( const std::vector &isotopic_cluster_charge_pairs, bool reset); }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicData.hpp000664 001750 001750 00000015275 15100504560 032650 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { using IsotopeListCstIterator = QList>::const_iterator; using IsotopeListIterator = QList>::iterator; using IsotopeListCstIteratorPair = std::pair; /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IsotopicData */ class DECLSPEC IsotopicData: public QObject { Q_OBJECT friend class IsotopicDataBaseHandler; friend class IsotopicDataLibraryHandler; friend class IsotopicDataUserConfigHandler; friend class IsotopicDataManualConfigHandler; Q_PROPERTY(std::size_t size READ size) public: Q_INVOKABLE IsotopicData(QObject *parent = nullptr); // Pseudo copy constructor Q_INVOKABLE IsotopicData(const IsotopicData &other, QObject *parent = nullptr); virtual ~IsotopicData(); Q_INVOKABLE void appendNewIsotope(IsotopeQSPtr isotope_qsp, bool update_maps = true); Q_INVOKABLE bool insertNewIsotope(IsotopeQSPtr isotope_qsp, qsizetype index, bool update_maps = true); Q_INVOKABLE void appendNewIsotopes(const QList &isotopes, bool update_maps = true); IsotopeListCstIterator eraseIsotopes(qsizetype begin_index, qsizetype end_index, bool update_maps = true); ///////////////////////////////////////////////////////// /// Function to be run each time something is loaded or anything is changed /// int the m_isotopes vector. Q_INVOKABLE bool updateMonoMassMap(const QString &symbol); Q_INVOKABLE std::size_t updateMonoMassMap(); Q_INVOKABLE bool updateAvgMassMap(const QString &symbol); Q_INVOKABLE std::size_t updateAvgMassMap(); Q_INVOKABLE double computeAvgMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; Q_INVOKABLE void updateMassMaps(const QString &symbol); Q_INVOKABLE std::size_t updateMassMaps(); ///////////////////////////////////////////////////////// /// All the functions needed to access the various bits from the m_isotopes /// vector of IsotopeQSPtr instances. // Get the monoisotopic mass of carbon. Q_INVOKABLE double getMonoMassBySymbol(const QString &symbol, bool &ok) const; Q_INVOKABLE double getMonoMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; Q_INVOKABLE double getAvgMassBySymbol(const QString &symbol, bool &ok) const; Q_INVOKABLE double getCumulatedProbabilitiesBySymbol(const QString &symbol, ErrorList *error_list_p) const; Q_INVOKABLE double getCumulatedProbabilities(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const; IsotopeListCstIteratorPair getIsotopesBySymbol(const QString &symbol) const; Q_INVOKABLE qsizetype getIsotopeCountBySymbol(const QString &symbol) const; IsotopeListCstIteratorPair getIsotopesByName(const QString &name) const; std::vector getUniqueSymbolsInOriginalOrder() const; Q_INVOKABLE bool containsSymbol(const QString &symbol, int &count) const; Q_INVOKABLE bool containsName(const QString &name, int &count) const; Q_INVOKABLE QString isotopesAsStringBySymbol(const QString &symbol) const; Q_INVOKABLE bool isMonoMassIsotope(IsotopeCstQSPtr isotope_cqsp); const QList> &getIsotopes() const; IsotopicData &operator=(const IsotopicData &other); bool operator==(const IsotopicData &other) const; bool operator!=(const IsotopicData &other) const; // The number of isotopes in m_isotopes. qsizetype size() const; bool isValid(); // The number of different symbols (that is, chemical elements) in the data qsizetype getUniqueSymbolsCount() const; bool validate(ErrorList *error_list_p) const; bool validateBySymbol(const QString &symbol, ErrorList *error_list_p) const; bool validateAllBySymbol(ErrorList *error_list_p) const; void replace(IsotopeQSPtr old_isotope_sp, IsotopeQSPtr new_isotope_sp); void clearSymbolMassMaps(); void clear(); static void registerJsConstructor(QJSEngine *engine); protected: // The container should never be sorted as we want to keep the order of the // isotopes in the way the vector has been populated, either by looking into // the IsoSpec library tables or by reading data from a user-configured file. // QList m_isotopes; QList> m_isotopes; // std::map m_symbolMonoMassMap; QMap m_symbolMonoMassMap; // std::map m_symbolAvgMassMap; QMap m_symbolAvgMassMap; mutable bool m_isValid = false; }; typedef std::shared_ptr IsotopicDataSPtr; typedef std::shared_ptr IsotopicDataCstSPtr; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: IsotopicData */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, IsotopicData) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp000664 001750 001750 00000004631 15100504560 034733 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once #include #include #include /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC IsotopicDataBaseHandler { public: IsotopicDataBaseHandler(const QString &file_name = QString()); IsotopicDataBaseHandler(IsotopicDataSPtr isotopic_data_sp, const QString &file_name = QString()); virtual ~IsotopicDataBaseHandler(); void setFileName(const QString &file_name); QString getFileName(); IsotopicDataSPtr getIsotopicData(); virtual qsizetype loadData(const QString &file_name = QString()) = 0; virtual qsizetype writeData(const QString &file_name = QString()) = 0; protected: IsotopicDataSPtr msp_isotopicData = nullptr; QString m_fileName; virtual qsizetype checkConsistency() = 0; }; typedef std::shared_ptr IsotopicDataBaseHandlerSPtr; typedef std::shared_ptr IsotopicDataBaseHandlerCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp000664 001750 001750 00000004573 15100504560 035413 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC IsotopicDataLibraryHandler : public IsotopicDataBaseHandler { public: IsotopicDataLibraryHandler(); IsotopicDataLibraryHandler(IsotopicDataSPtr isotopic_data_sp); virtual ~IsotopicDataLibraryHandler(); virtual qsizetype loadData(const QString &filename = QString()) override; virtual qsizetype loadData(qsizetype &count_non_isotope_skipped_items); virtual qsizetype writeData(const QString &file_name = QString()) override; protected: virtual qsizetype checkConsistency() override; }; typedef std::shared_ptr IsotopicDataLibraryHandlerSPtr; typedef std::shared_ptr IsotopicDataLibraryHandlerCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp000664 001750 001750 00000006600 15100504560 036343 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include #include #include /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { // This class specializes IsotopicDataBaseHandler by providing function // specific for the user's manual configuration of chemical formula associated // with elements' isotopic data. The format of the data on file is nothing // like the format used to store Library- or User-Config- isotopic data. // File format: // //[Element] // symbol C count 100 //[Isotopes] 2 // mass 12.000 prob 0.9899 // mass 13.000 prob 0.010 using SymbolCountMap = std::map; using SymbolCountMapIter = SymbolCountMap::iterator; class DECLSPEC IsotopicDataManualConfigHandler : public IsotopicDataBaseHandler { public: IsotopicDataManualConfigHandler(const QString &file_name = QString()); IsotopicDataManualConfigHandler(IsotopicDataSPtr isotopic_data_sp, const QString &file_name = QString()); virtual ~IsotopicDataManualConfigHandler(); virtual void setSymbolCountMap(const SymbolCountMap &map); virtual const SymbolCountMap &getSymbolCountMap() const; virtual qsizetype loadData(const QString &file_name = QString()) override; virtual qsizetype writeData(const QString &file_name = QString()) override; bool newChemicalSet(const QString &symbol, int element_count, const QList &isotopes, bool update_maps = true); QString craftFormula() const; protected: // Helper data to store symbol/count data. SymbolCountMap m_symbolCountMap; virtual qsizetype checkConsistency() override; }; typedef std::shared_ptr IsotopicDataManualConfigHandlerSPtr; typedef std::shared_ptr IsotopicDataManualConfigHandlerCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS source/XpertMassCore/includes/MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp000664 001750 001750 00000004531 15100504560 036045 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once #include #include #include /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC IsotopicDataUserConfigHandler : public IsotopicDataBaseHandler { public: IsotopicDataUserConfigHandler(const QString &file_name = QString()); IsotopicDataUserConfigHandler(IsotopicDataSPtr isotopic_data_sp, const QString &file_name = QString()); virtual ~IsotopicDataUserConfigHandler(); virtual qsizetype loadData(const QString &file_name = QString()) override; virtual qsizetype writeData(const QString &file_name = QString()) override; protected: virtual qsizetype checkConsistency() override; }; typedef std::shared_ptr IsotopicDataUserConfigHandlerSPtr; typedef std::shared_ptr IsotopicDataUserConfigHandlerCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassCollection.hpp000664 001750 001750 00000005443 15100504560 033200 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/globals.hpp" /////////////////////// libXpertMassGui includes /////////////////////// Local includes /////////////////////// Qt includes namespace MsXpS { namespace libXpertMassCore { class DECLSPEC MassCollection { public: MassCollection(const QString &name); MassCollection(const QString &name, const QString &mass_text); MassCollection(const QString &name, const std::vector &masses); MassCollection(const MassCollection &other); ~MassCollection(); MassCollection &operator=(const MassCollection &other); void setName(const QString &name); const QString &getName() const; void setComment(const QString &comment); const QString &getComment() const; const std::vector &getMassesCstRef() const; std::vector &getMassesRef(); double getMassAtIndex(std::size_t index) const; int textToMasses(const QString &text, ErrorList *error_list_p); QString massesToText(); void sortAscending(); void sortDescending(); void addMass(double mass); void removeLessThan(double threshold); std::size_t size() const; bool validate(ErrorList *error_list_p) const; bool isValid() const; protected: QString m_name; QString m_comment; std::vector m_masses; mutable bool m_isValid = false; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp000664 001750 001750 00000006225 15100504560 034654 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Std lib includes /////////////////////// Qt includes #include #include #include /////////////////////// PAPPSO includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { typedef std::shared_ptr QCborStreamReaderSPtr; typedef std::shared_ptr QCborStreamWriterSPtr; class DECLSPEC MassDataCborBaseHandler: public QObject { Q_OBJECT public: MassDataCborBaseHandler(QObject *parent_p); ~MassDataCborBaseHandler(); void setInputFileName(const QString &file_name); void setOutputFileName(const QString &file_name); static Enums::MassDataType readMassDataType(const QString &input_file_name); static Enums::MassDataType readMassDataType(const QByteArray &byte_array); static Enums::MassDataType readMassDataType(QCborStreamReaderSPtr &reader_sp); void setMassDataType(Enums::MassDataType mass_data_type); Enums::MassDataType getMassDataType() const; virtual bool readFile(const QString &input_file_name = QString()); virtual bool readByteArray(const QByteArray &byte_array); virtual bool writeFile(const QString &output_file_name = QString()); virtual void writeByteArray(QByteArray &byte_array); void setTitle(const QString &title); QString getTitle() const; protected: Enums::MassDataType m_massDataType = Enums::MassDataType::NOT_SET; QString m_title = QString(); QString m_currentKey; QCborStreamReaderSPtr msp_reader = nullptr; QCborStreamWriterSPtr msp_writer = nullptr; QString m_inputFileName = QString(); QString m_outputFileName = QString(); virtual bool decodeStream(QCborStreamReaderSPtr &reader_sp); }; } // namespace libXpertMassCore } // namespace MsXpS source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp000664 001750 001750 00000006335 15100504560 036353 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Std lib includes /////////////////////// Qt includes #include #include #include /////////////////////// PAPPSO includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { // enum class MassDataType //{ // NOT_SET = 0, // MASS_SPECTRUM, // DRIFT_SPECTRUM, // TIC_CHROMATOGRAM, // XIC_CHROMATOGRAM, // MZ_RT_COLORMAP, // DT_RT_COLORMAP, // DT_MZ_COLORMAP, //}; class DECLSPEC MassDataCborMassSpectrumHandler : public MassDataCborBaseHandler { Q_OBJECT public: MassDataCborMassSpectrumHandler(QObject *parent_p); MassDataCborMassSpectrumHandler(QObject *parent_p, const pappso::Trace &trace); ~MassDataCborMassSpectrumHandler(); virtual bool readFile(const QString &input_file_name = QString()) override; virtual bool readByteArray(const QByteArray &byte_array) override; virtual bool writeFile(const QString &output_file_name = QString()) override; virtual void writeByteArray(QByteArray &byte_array) override; void setXLabel(const QString &label); QString getXLabel() const; void setYLabel(const QString &label); QString getYLabel() const; void setTrace(const pappso::Trace &trace); pappso::Trace getTrace() const; void clearTrace(); void setTraceColor(const QByteArray &color_byte_array); QByteArray getTraceColor() const; protected: pappso::Trace m_trace; QByteArray m_colorByteArray; QByteArray m_xBase64Data; QByteArray m_yBase64Data; QString m_xLabel; QString m_yLabel; virtual bool decodeStream(QCborStreamReaderSPtr &reader_sp) override; }; typedef std::shared_ptr MassDataCborMassSpectrumHandlerSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassDataClient.hpp000664 001750 001750 00000005243 15100504560 033113 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ***************************************************************************** * This specific code is a port to C++ of the Envemind Python code by Radzinski * and colleagues of IsoSpec fame (Lacki, Startek and company :-) * * See https://github.com/PiotrRadzinski/envemind. * ***************************************************************************** * * END software license */ #pragma once /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC MassDataClient: public QObject { Q_OBJECT public: explicit MassDataClient(const QString &ip_address, int port_number, QObject *parent = nullptr); virtual ~MassDataClient(); QString getStatus(); void forcefullyDisconnect(); QString getIpAddress() const; int getPortNumber() const; protected slots: void reportError(QAbstractSocket::SocketError socket_error); virtual void readData(); signals: void reportErrorSignal(const QString &error); void newDataSignal(const QByteArray &byte_array); void hostFoundSignal(); void connectedSignal(); void disconnectedSignal(); protected: QDataStream m_inStream; QByteArray m_data; QString m_ipAddress; int m_portNumber; QTcpSocket *mp_tcpSocket = nullptr; bool m_forcefulDisconnection = false; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassDataServer.hpp000664 001750 001750 00000004755 15100504560 033152 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ***************************************************************************** * This specific code is a port to C++ of the Envemind Python code by Radzinski * and colleagues of IsoSpec fame (Lacki, Startek and company :-) * * See https://github.com/PiotrRadzinski/envemind. * ***************************************************************************** * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC MassDataServer: public QTcpServer { Q_OBJECT public: MassDataServer(QObject *parent = nullptr); virtual ~MassDataServer(); bool start(); QString getIpAddress() const; int getPortNumber() const; void serveData(const QByteArray &byte_array_to_serve); bool hasReadyClient() const; signals: void writtenDataSignal(std::size_t written_bytes); protected: virtual void incomingConnection(qintptr socketDescriptor) override; protected: QString m_ipAddress; int m_portNumber; QList m_clients; QSet m_readyClients; void error(QTcpSocket::SocketError socket_error); }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassPeakShaper.hpp000664 001750 001750 00000006136 15100504560 033130 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes /////////////////////// Qt includes /////////////////////// pappsomspp includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MassPeakShaperConfig.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC MassPeakShaper { public: MassPeakShaper(); MassPeakShaper(double mz, double intensity, const MassPeakShaperConfig &config); MassPeakShaper(const pappso::DataPoint &data_point, const MassPeakShaperConfig &config); MassPeakShaper(const MassPeakShaper &); virtual ~MassPeakShaper(); void setPeakCentroid(const pappso::DataPoint &data_point); const pappso::DataPoint &getPeakCentroid() const; void setConfig(const MassPeakShaperConfig &config); const MassPeakShaperConfig &getConfig() const; const pappso::Trace &getTrace() const; int computePeakShape(); static pappso::Trace computePeakShape(double mz, double intensity, const MassPeakShaperConfig &config); int computeGaussianPeakShape(); static pappso::Trace computeGaussianPeakShape( double mz, double intensity, const MassPeakShaperConfig &config); int computeLorentzianPeakShape(); static pappso::Trace computeLorentzianPeakShape( double mz, double intensity, const MassPeakShaperConfig &config); double intensityAt(double mz, pappso::PrecisionPtr precision_p, bool &ok); QString shapetoString(); bool shapeToFile(const QString &file_name); protected: pappso::DataPoint m_peakCentroid; MassPeakShaperConfig m_config; pappso::Trace m_trace; void clearTrace(); }; typedef std::shared_ptr MassPeakShaperSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp000664 001750 001750 00000014015 15100504560 034251 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes /////////////////////// Qt includes /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { extern int FWHM_PEAK_SPAN_FACTOR; /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: MassPeakShaperConfig */ class DECLSPEC MassPeakShaperConfig: public QObject { Q_OBJECT Q_PROPERTY(double resolution READ getResolution WRITE setResolution MEMBER m_resolution) Q_PROPERTY(double fwhm READ getFwhm WRITE setFwhm MEMBER m_fwhm) Q_PROPERTY( Enums::MassPeakWidthLogic massPeakWidthLogic READ getMassPeakWidthLogic WRITE setMassPeakWidthLogic MEMBER m_massPeakWidthLogic) Q_PROPERTY(double referencePeakMz READ getReferencePeakMz WRITE setReferencePeakMz MEMBER m_referencePeakMz) Q_PROPERTY( int pointCount READ getPointCount WRITE setPointCount MEMBER m_pointCount) Q_PROPERTY(bool withBins READ isWithBins WRITE setWithBins MEMBER m_withBins) Q_PROPERTY(int binSizeDivisor READ getBinSizeDivisor WRITE setBinSizeDivisor MEMBER m_binSizeDivisor) Q_PROPERTY(double binSize READ getBinSize WRITE setBinSize MEMBER m_binSize) Q_PROPERTY(bool isBinSizeFixed READ getBinSizeFixed WRITE setBinSizeFixed MEMBER m_isBinSizeFixed) Q_PROPERTY(double mzStep READ getMzStep WRITE setMzStep MEMBER m_mzStep) Q_PROPERTY( Enums::MassPeakShapeType massPeakShapeType READ getMassPeakShapeType WRITE setMassPeakShapeType MEMBER m_massPeakShapeType) public: explicit MassPeakShaperConfig(QObject *parent = nullptr); // Pseudo copy constructor. explicit MassPeakShaperConfig(const MassPeakShaperConfig &other, QObject *parent = nullptr); virtual ~MassPeakShaperConfig(); MassPeakShaperConfig &initialize(const MassPeakShaperConfig &other); MassPeakShaperConfig *clone(const MassPeakShaperConfig &other, QObject *parent = nullptr) const; void operator=(const MassPeakShaperConfig &other) = delete; void setResolution(int resolution); double getResolution() const; int resolution(bool *ok); // For the gaussion, that is the entirety of the fwhm. void setFwhm(double value); double getFwhm() const; double fwhm(bool *ok); // For the lorentzian, that is half of the fwhm. double halfFwhm(bool *ok); void setReferencePeakMz(double mz); double getReferencePeakMz() const; void setBinSize(double bin_size); double binSize(bool *ok); double getBinSize() const; void setWithBins(bool with_bins); bool isWithBins() const; void setBinSizeFixed(bool is_fixed); bool getBinSizeFixed(); void setBinSizeDivisor(int divisor); int getBinSizeDivisor() const; void setPointCount(int point_count); int getPointCount() const; void setMassPeakShapeType(Enums::MassPeakShapeType mass_peak_shape_type); Enums::MassPeakShapeType getMassPeakShapeType() const; void setMassPeakWidthLogic(Enums::MassPeakWidthLogic logic); Enums::MassPeakWidthLogic getMassPeakWidthLogic() const; double c(bool *ok); double a(bool *ok); double gamma(bool *ok); void setMzStep(double step); double getMzStep() const; double mzStep(bool *ok); bool resolve(libXpertMassCore::ErrorList &error_list); void reset(); QString toString(); static void registerJsConstructor(QJSEngine *engine); protected: int m_resolution = 0; double m_fwhm = 0; Enums::MassPeakWidthLogic m_massPeakWidthLogic = Enums::MassPeakWidthLogic::NOT_SET; // The reference m/z value is the m/z value of the peak that is considered // to be the main peak in an isotopic cluster, for example. That is // typically the peak that is the most intense. This peak's intensity is // typically used to compute the FWHM on the basis of the resolution, for // example (see function fwhm(). When computing a Gaussian shape for a // single peak, then that reference peak's m/z value needs to be set as the // peak centroid for which the computation is performed. double m_referencePeakMz = 0; // Number of points to use to shape the peak int m_pointCount = 0; bool m_withBins = false; int m_binSizeDivisor = 6; double m_binSize = 0; bool m_isBinSizeFixed = false; // The delta bewteen two consecutive data points double m_mzStep = 0; // Type (GAUSSIAN | LORENTZIAN) of the peak shape Enums::MassPeakShapeType m_massPeakShapeType = Enums::MassPeakShapeType::NOT_SET; }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, MassPeakShaperConfig) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Modif.hpp000664 001750 001750 00000013014 15100504560 031310 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/PropListHolder.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { class Modif; typedef std::unique_ptr ModifUPtr; typedef std::unique_ptr ModifCstUPtr; typedef std::shared_ptr ModifSPtr; typedef std::shared_ptr ModifCstSPtr; typedef std::weak_ptr ModifWPtr; typedef std::weak_ptr ModifCstWPtr; using UuidModifCstWPtrPair = std::pair; using UuidModifWPtrPair = std::pair; class Monomer; using MonomerCstSPtr = std::shared_ptr; class DECLSPEC Modif : public PropListHolder { public: Modif(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version); Modif(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &modif_name = QString(), const QString &formula_string = QString(), double mono = 0.0, double avg = 0.0); Modif(const Modif &other); virtual ~Modif(); //////////////// THE POLCHEMDEF ///////////////////// void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); const PolChemDefCstSPtr &getPolChemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); QString getName() const; //////////////// THE FORMULA ///////////////////// void setFormula(const QString &formula_string); const QString &getFormula() const; QString formula(bool with_title) const; //////////////// THE TARGETS ///////////////////// void setTargets(const QString &targets); QString getTargets() const; bool doesTargetMonomer(const QString &code) const; //////////////// THE MAX MODIF COUNT ///////////////////// void setMaxCount(int); int getMaxCount() const; //////////////// OPERATORS ///////////////////// using PropListHolder::operator=; Modif &operator=(const Modif &modif); bool operator==(const Modif &modif) const; bool operator!=(const Modif &modif) const; //////////////// VALIDATIONS ///////////////////// ModifCstSPtr getFromPolChemDefByName() const; Enums::PolChemDefEntityStatus isKnownByNameInPolChemDef() const; QString validateTargets(const QString &targets_string, bool simplify, bool &ok) const; QString validateTargets(bool simplify, bool &ok); bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// MASS OPERATIONS ///////////////////// bool calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const; bool calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp); Modif &calculateMasses(bool &ok, const IsotopicDataCstSPtr &isotopic_data_csp); const Modif &accountMasses(double *mono_p = nullptr, double *avg_p = nullptr, int times = 1) const; const Modif &accountMasses(double &mono, double &avg, int times = 1) const; double getMass(Enums::MassType mass_type); //////////////// XML ///////////////////// bool renderXmlMdfElement(const QDomElement &element, int version); QString formatXmlMdfElement(int offset, const QString &indent = Utils::xmlIndentationToken) const; //////////////// UTILS ///////////////////// void clear(); QString toString() const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; QString m_formula = ""; QString m_targets = ""; int m_maxCount = -1; double m_mono = 0.0; double m_avg = 0.0; mutable bool m_isValid = false; }; } // namespace libXpertMassCore } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Modif); extern int modifMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Modif *); extern int modifPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Monomer.hpp000664 001750 001750 00000016350 15100504560 031674 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/PropListHolder.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" namespace MsXpS { namespace libXpertMassCore { class Monomer; typedef Monomer *MonomerRPtr; typedef const Monomer *MonomerCstRPtr; typedef std::unique_ptr MonomerUPtr; typedef std::unique_ptr MonomerCstUPtr; typedef std::shared_ptr MonomerSPtr; typedef std::shared_ptr MonomerCstSPtr; typedef std::weak_ptr MonomerWPtr; typedef std::weak_ptr MonomerCstWPtr; using UuidMonomerCstWPtrPair = std::pair; using UuidMonomerWPtrPair = std::pair; class DECLSPEC Monomer : public PropListHolder { public: Monomer(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version); Monomer(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &code = QString(), const QString &formula_string = QString(), double mono = 0.0, double avg = 0.0); Monomer(const Monomer &other); virtual ~Monomer(); //////////////// THE POLCHEMDEF ///////////////////// void setPolChemDefCstSPtr(PolChemDefCstSPtr &pol_chem_def_csp); const PolChemDefCstSPtr &getPolChemDefCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); QString getName() const; //////////////// THE CODE ///////////////////// void setCode(const QString &code); QString getCode() const; bool checkCodeSyntax(int code_length = -1) const; //////////////// THE FORMULA ///////////////////// void setFormula(const QString &formula_string); const QString &getFormula() const; //////////////// THE MODIFICATIONS ///////////////////// const std::vector &getModifsCstRef() const; std::vector modifNamesInOrder() const; bool isModifTarget(const Modif &modif) const; QString modify(const Modif &modif, bool override, ErrorList *error_list_p); QString modify(const QString &modif_name, bool override, ErrorList *error_list_p); bool unmodify(const QString &uuid); bool unmodify(); bool isModified() const; int countModifsByName(const QString &name); //////////////// OPERATORS ///////////////////// using PropListHolder::operator=; Monomer &operator=(const Monomer &other); bool operator==(const Monomer &) const; bool operator!=(const Monomer &) const; //////////////// VALIDATIONS ///////////////////// MonomerSPtr getFromPolChemDefByName() const; MonomerSPtr getFromPolChemDefByCode() const; Enums::PolChemDefEntityStatus isKnownByNameInPolChemDef() const; Enums::PolChemDefEntityStatus isKnownByCodeInPolChemDef() const; bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// MASS OPERATIONS ///////////////////// bool calculateMasses( const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg, Enums::ChemicalEntity monomer_chemical_entities = Enums::ChemicalEntity::NONE) const; bool calculateMasses( const IsotopicDataCstSPtr &isotopic_data_csp, Enums::ChemicalEntity monomer_chemical_entities = Enums::ChemicalEntity::NONE); Monomer &calculateMasses( bool &ok, const IsotopicDataCstSPtr &isotopic_data_csp, Enums::ChemicalEntity monomer_chemical_entities = Enums::ChemicalEntity::NONE); const Monomer &accountMasses(double *mono_p = nullptr, double *avg_p = nullptr, int times = 1) const; const Monomer &accountMasses(double &mono, double &avg, int times = 1) const; double getMass(Enums::MassType mass_type) const; //////////////// FORMULA OPERATIONS ///////////////////// QString calculateFormula( Enums::ChemicalEntity monomer_chemical_entities = Enums::ChemicalEntity::NONE) const; //////////////// XML DATA LOADING WRITING ///////////////////// bool renderXmlMnmElement(const QDomElement &element, int version); QString formatXmlMnmElement(int offset, const QString &indent = Utils::xmlIndentationToken) const; bool renderXmlMonomerElement(const QDomElement &element, int version); QString formatXmlMonomerElement( int offset, const QString &indent = Utils::xmlIndentationToken) const; //////////////// UTILS ///////////////////// QString storeModif(const ModifSPtr &modif_sp); QString storeModif(const Modif &modif); bool hasModif(const ModifSPtr &modif_sp) const; bool hasUuid(const ModifSPtr &modif_sp) const; ModifSPtr getModifForUuid(const QString &uuid) const; QString getUuidForModif(const ModifSPtr &modif_sp) const; std::vector getAllModifUuids() const; void clear(); QString toString() const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name = ""; QString m_code = ""; QString m_formula = ""; double m_mono = 0.0; double m_avg = 0.0; std::vector m_modifs; // We need in code that uses this class, to constantly keep at hand // the Modif instances that are involved in Monomer_s. For example, // we need to store the Modif pointers as strings in QListWidget items // (Qt:UserRole). // We thus make use of the QUuid class to craft a Uuid string that // we associate to the Modif weak pointer that in turn relates // to the Modif shared pointer. std::vector m_uuidModifPairs; mutable bool m_isValid = false; private: void cleanupModifs(); bool removeModif(ModifSPtr modif_sp); }; } // namespace libXpertMassCore } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Monomer); extern int monomerMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/MonomerDictionary.hpp000664 001750 001750 00000004561 15100504560 033723 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC MonomerDictionary { public: MonomerDictionary(QString = QString(), const QStringList & = QStringList(), int = -1, int = -1); ~MonomerDictionary(); void setFilePath(QString &); void setInputChainStringList(const QStringList &); void setInputCodeLength(int); void setOutputCodeLength(int); bool loadDictionary(); QStringList *translate(const QStringList & = QStringList()); protected: bool isLineProperSectionDivider(const QString &); void skipSection(QTextStream *); int parseSection(QTextStream *); QHash m_dictionaryHash; bool m_dictionaryLoaded; QString m_filePath; QStringList m_inputChainStringList; int m_inputCodeLength; int m_outputCodeLength; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Oligomer.hpp000664 001750 001750 00000014416 15100504560 032036 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/CrossLink.hpp" #include "MsXpS/libXpertMassCore/Sequence.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" #include "MsXpS/libXpertMassCore/CalcOptions.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { // Forward declaration class Polymer; class DECLSPEC Oligomer : public PropListHolder { friend class Ionizer; public: Oligomer(PolymerCstQSPtr polymer_cqsp, const QString &name = QString(), const QString &description = QString(), bool is_modified = false, const Ionizer &ionizer = Ionizer(), const CalcOptions &calc_options = CalcOptions()); Oligomer(const Oligomer &other); virtual ~Oligomer(); //////////////// THE POLYMER ///////////////////// void setPolymerCstSPtr(PolymerCstQSPtr polymer_cqsp); const PolymerCstQSPtr &getPolymerCstSPtr() const; //////////////// THE NAME ///////////////////// void setName(const QString &name); QString getName() const; //////////////// THE DESCRIPTION ///////////////////// void setDescription(const QString &description); QString getDescription() const; //////////////// MODIFICATION STATUS ///////////////////// void setModified(bool is_modified = true); bool isModified(bool deep = false) const; std::size_t modifiedMonomerCount() const; //////////////// THE IONIZER ///////////////////// void setIonizer(const Ionizer &ionizer); const Ionizer &getIonizerCstRef() const; Ionizer &getIonizerRef(); Enums::IonizationOutcome ionize(); Enums::IonizationOutcome deionize(); Enums::IonizationOutcome molecularMasses(double &mono, double &avg) const; //////////////// THE SEQUENCE RANGES ///////////////////// void setIndexRanges(const IndexRangeCollection &index_ranges); void setIndexRange(const IndexRange &index_range); const IndexRangeCollection &getIndexRangeCollectionCstRef() const; IndexRangeCollection &getIndexRangeCollectionRef(); void setStartAndStopIndices(std::size_t start_index, std::size_t stop_index); void setStartIndex(int start_index); qsizetype startIndex(bool &ok) const; void setStopIndex(int stop_index); qsizetype stopIndex(bool &ok) const; //////////////// THE CALCULATION OPTIONS ///////////////////// void setCalcOptions(const CalcOptions &calc_options); const CalcOptions &getCalcOptionsCstRef() const; CalcOptions &getCalcOptionsRef(); //////////////// THE MASSES ///////////////////// void setMass(Enums::MassType mass_type, double mass); void setMasses(double mono, double avg); double getMass(Enums::MassType mass_type) const; double &getMassRef(Enums::MassType mass_type); //////////////// THE CROSS-LINKS ///////////////////// const std::vector &getCrossLinksCstRef() const; std::vector &getCrossLinksRef(); bool addCrossLink(CrossLinkSPtr cross_link_sp); //////////////// THE PARTIAL CLEAVAGE ///////////////////// void setPartialCleavage(std::size_t partial_cleavage); std::size_t getPartialCleavage() const; //////////////// THE FORMULA ///////////////////// void setFormula(const Formula &formula); void setFormula(const QString &formula_string); const Formula &getFormulaCstRef() const; Formula &getFormulaRef(); //////////////// THE MONOMERS ///////////////////// MonomerSPtr getLeftEndMonomerCstSPtr() const; MonomerSPtr getRightEndMonomerCstSPtr() const; MonomerSPtr getMonomerCstSPtrAt(std::size_t index) const; //////////////// OPERATORS ///////////////////// using PropListHolder::operator=; Oligomer &operator=(const Oligomer &other); //////////////// ELEMENTAL CALCULATION FUNCTIONS ///////////////////// virtual QString elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const; virtual QString elementalComposition() const; //////////////// MASS CALCULATION FUNCTIONS ///////////////////// virtual bool calculateMasses(); virtual bool calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer); std::size_t size(); bool encompasses(std::size_t index) const; bool encompasses(MonomerCstRPtr monomer_crp) const; bool encompasses(MonomerSPtr monomer_sp) const; QString toString() const; protected: PolymerCstQSPtr mcsp_polymer; QString m_name; QString m_description; mutable bool m_isModified = false; Ionizer m_ionizer; CalcOptions m_calcOptions; double m_mono = 0; double m_avg = 0; // The container of the the cross-links that are involved in the // formation of the cross-linked oligomer (the CrossLinkSPtr // instances are shared with mcsp_polymer). std::vector m_crossLinks; std::size_t m_partialCleavage = 0; Formula m_formula; }; typedef std::shared_ptr OligomerSPtr; typedef std::shared_ptr OligomerCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/OligomerCollection.hpp000664 001750 001750 00000007343 15100504560 034053 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Oligomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC OligomerCollection : public QObject { Q_OBJECT public: OligomerCollection(const QString &name = QString(), PolymerCstQSPtr polymer_cqsp = nullptr, Enums::MassType mass_type = Enums::MassType::BOTH); OligomerCollection(const OligomerCollection &other); virtual ~OligomerCollection(); void setPolymer(PolymerCstQSPtr polymer_cqsp); const PolymerCstQSPtr getPolymer() const; void setName(const QString &name); const QString &getName() const; void setComment(const QString &comment); const QString &getComment() const; const std::vector &getOligomersCstRef() const; std::vector &getOligomersRef(); void setMassType(Enums::MassType mass_type); Enums::MassType getMassType() const; OligomerSPtr findOligomerEncompassing(std::size_t monomer_index, std::size_t &oligomer_index); OligomerSPtr findOligomerEncompassing(MonomerCstSPtr monomer_csp, std::size_t &oligomer_index); OligomerSPtr findOligomerEncompassing(MonomerSPtr monomer_sp, std::size_t &oligomer_index); // In the methods below, Enums::MassType is by default MASS_NONE, // which means that the value of the variable is to be read in the // member m_massType. QString allOligomerMassesToString(Enums::MassType mass_type); static bool monoMassCompare(const OligomerSPtr o1_sp, const OligomerSPtr o2_sp); static bool avgMassCompare(const OligomerSPtr o1_sp, const OligomerSPtr o2_sp); void sortAscending(); void sortDescending(); std::size_t size() const; QString toString() const; void clear(); OligomerCollection &operator=(const OligomerCollection &other); bool operator==(const OligomerCollection &other) const; bool operator!=(const OligomerCollection &other) const; protected: QString m_name; QString m_comment; PolymerCstQSPtr mcsp_polymer; Enums::MassType m_massType; std::vector m_oligomers; }; typedef std::shared_ptr OligomerCollectionSPtr; typedef std::shared_ptr OligomerCollectionCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/OligomerPair.hpp000664 001750 001750 00000005342 15100504560 032650 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/PropListHolder.hpp" #include "MsXpS/libXpertMassCore/Oligomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC OligomerPair : public PropListHolder { public: OligomerPair(const QString &name, const OligomerSPtr first_csp, const OligomerSPtr second_csp, Enums::MassType mass_type, double error, bool isMatching = false); OligomerPair(const OligomerPair &); virtual ~OligomerPair(); QString getName(); const OligomerSPtr getFirst() const; const OligomerSPtr getSecond() const; void setMassType(Enums::MassType mass_type); Enums::MassType getMassType() const; void setError(double); double getError() const; void setMatching(bool isMatching); bool isMatching() const; double getFirstMass(); double getSecondMass(); int firstCharge(); int secondCharge(); //////////////// OPERATORS ///////////////////// using PropListHolder::operator=; OligomerPair &operator=(const OligomerPair &other); protected: QString m_name; OligomerSPtr msp_first; OligomerSPtr msp_second; Enums::MassType m_massType; double m_error; bool m_isMatching; }; typedef std::shared_ptr OligomerPairSPtr; typedef std::shared_ptr OligomerPairCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/PkaPhPi.hpp000664 001750 001750 00000006557 15100504560 031564 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" #include "MsXpS/libXpertMassCore/ChemicalGroup.hpp" #include "MsXpS/libXpertMassCore/PropListHolder.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC PkaPhPi : public PropListHolder { public: PkaPhPi(PolChemDefCstSPtr pol_chem_def_csp, PolymerCstQSPtr polymer_cqsp, const CalcOptions &calc_options); ~PkaPhPi(); void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolChemDefCstSPtr() const; void setMonomers(std::vector &&monomers); void setMonomers(std::vector &&monomers); void setMonomers(std::vector &monomers); const std::vector &getMonomersCstRef() const; void setModifs(std::vector &&modifs); void setModifs(std::vector &&modifs); void setModifs(std::vector &modifs); const std::vector &getModifs() const; void setPh(double); double ph(); double pi(); double positiveCharges(); double negativeCharges(); void setCalcOptions(const CalcOptions &); void setMonomerList(QList *); void setModifList(QList *); int calculateCharges(); int calculatePi(); double calculateChargeRatio(double, bool); int accountPolymerEndModif(Enums::ChemicalEntity polymer_chem_ent, const ChemicalGroup &chemical_group); int accountMonomerModif(const Monomer &, ChemicalGroup &); using PropListHolder::operator=; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; std::vector m_monomers; std::vector m_modifs; double m_ph = 7; double m_pi = 7; PolymerCstQSPtr mcsp_polymer; CalcOptions m_calcOptions; double m_positiveCharges = 0; double m_negativeCharges = 0; bool *mp_aborted = nullptr; int *mp_progress = nullptr; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/PkaPhPiDataParser.hpp000664 001750 001750 00000003666 15100504560 033531 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC PkaPhPiDataParser { public: PkaPhPiDataParser(const PolChemDefCstSPtr &pol_chem_def_csp, const QString &file_path = QString()); ~PkaPhPiDataParser(); void setFilePath(const QString &); const QString &filePath(); bool renderXmlFile(std::vector &monomers, std::vector &modifs); protected: PolChemDefCstSPtr mcsp_polChemDef; QString m_filePath; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/PolChemDef.hpp000664 001750 001750 00000021777 15100504560 032237 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once ////////////////////////////// Stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/PolChemDefSpec.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" namespace MsXpS { namespace libXpertMassCore { extern const int POL_CHEM_DEF_FILE_FORMAT_VERSION; class PolChemDef; typedef std::shared_ptr PolChemDefSPtr; typedef std::shared_ptr PolChemDefCstSPtr; //////////////////// MODIFS ////////////////////// //////////////////// MODIFS ////////////////////// class Modif; using ModifCstSPtr = std::shared_ptr; using ModifSPtr = std::shared_ptr< Modif>; typedef std::vector::iterator ModifsIterator; typedef std::vector::const_iterator ModifsCstIterator; //////////////////// MONOMERS ////////////////////// //////////////////// MONOMERS ////////////////////// class Monomer; using MonomerCstSPtr = std::shared_ptr; using MonomerSPtr = std::shared_ptr< Monomer>; typedef std::vector::iterator MonomersIterator; typedef std::vector::const_iterator MonomersCstIterator; //////////////////// CROSSLINKERS ////////////////////// //////////////////// CROSSLINKERS ////////////////////// class CrossLinker; using CrossLinkerCstSPtr = std::shared_ptr; using CrossLinkerSPtr = std::shared_ptr< CrossLinker>; typedef std::vector::iterator CrossLinkersIterator; typedef std::vector::const_iterator CrossLinkersCstIterator; //////////////////// CLEAVAGE AGENTS ////////////////////// //////////////////// CLEAVAGE AGENTS ////////////////////// class CleavageAgent; using CleavageAgentCstSPtr = std::shared_ptr; using CleavageAgentSPtr = std::shared_ptr< CleavageAgent>; typedef std::vector::iterator CleavageAgentsIterator; typedef std::vector::const_iterator CleavageAgentsCstIterator; //////////////////// FRAGMENTATION PATHWAYS ////////////////////// //////////////////// FRAGMENTATION PATHWAYS ////////////////////// class FragmentationPathway; using FragmentationPathwayCstSPtr = std::shared_ptr; using FragmentationPathwaySPtr = std::shared_ptr< FragmentationPathway>; typedef std::vector::iterator FragmentationPathwaysIterator; typedef std::vector::const_iterator FragmentationPathwaysCstIterator; class DECLSPEC PolChemDef { public: PolChemDef(); PolChemDef(const PolChemDefSpec &pol_chem_def_spec); virtual ~PolChemDef(); void setName(const QString &name); QString getName() const; void setXmlDataFilePath(const QString &file_path); QString getXmlDataFilePath() const; QString getXmlDataDirPath() const; void setIsotopicDataFilePath(const QString &file_path); QString getIsotopicDataFilePath() const; QString deduceIsotopicDataFilePath() const; void setLeftCap(const Formula &formula); const Formula &getLeftCap() const; void setRightCap(const Formula &formula); const Formula &getRightCap() const; void setCodeLength(int code_length); int getCodeLength() const; void setIonizer(const Ionizer &ionizer); const Ionizer &getIonizerCstRef() const; Ionizer &getIonizerRef(); void setIsotopicDataSPtr(IsotopicDataSPtr isotopic_data_sp); IsotopicDataCstSPtr getIsotopicDataCstSPtr() const; IsotopicDataSPtr getIsotopicDataSPtr(); //////////////////// MODIFS ////////////////////// //////////////////// MODIFS ////////////////////// const std::vector &getModifsCstRef() const; std::vector &getModifsRef(); ModifCstSPtr getModifCstSPtrByName(const QString &name) const; int getModifIndexByName(const QString &name) const; //////////////////// MODIFS ////////////////////// //////////////////// MONOMERS ////////////////////// //////////////////// MONOMERS ////////////////////// const std::vector &getMonomersCstRef() const; std::vector &getMonomersRef(); MonomerSPtr getMonomerCstSPtrByName(const QString &name) const; MonomerSPtr getMonomerCstSPtrByCode(const QString &code) const; int getMonomerIndexByName(const QString &name) const; int getMonomerIndexByCode(const QString &name) const; //////////////////// MONOMERS ////////////////////// //////////////////// CROSSLINKERS ////////////////////// //////////////////// CROSSLINKERS ////////////////////// const std::vector &getCrossLinkersCstRef() const; std::vector &getCrossLinkersRef(); CrossLinkerCstSPtr getCrossLinkerCstSPtrByName(const QString &name) const; //////////////////// CROSSLINKERS ////////////////////// //////////////////// CLEAVAGE AGENTS ////////////////////// //////////////////// CLEAVAGE AGENTS ////////////////////// const std::vector & getCleavageAgentsCstRef() const; std::vector &getCleavageAgentsRef(); CleavageAgentCstSPtr getCleavageAgentCstSPtrByName(const QString &name) const; int getCleavageAgentIndexByName(const QString &name) const; //////////////////// CLEAVAGE AGENTS ////////////////////// //////////////////// FRAGMENTATION PATHWAYS ////////////////////// //////////////////// FRAGMENTATION PATHWAYS ////////////////////// const std::vector & getFragmentationPathwaysCstRef() const; std::vector &getFragmentationPathwaysRef(); FragmentationPathwayCstSPtr getFragmentationPathwayCstSPtrByName(const QString &name) const; int getFragmentationPathwayIndexByName(const QString &name) const; //////////////////// FRAGMENTATION PATHWAYS ////////////////////// bool calculateDelimitedCodes(); const QString &getDelimitedCodes(); QStringList differenceBetweenMonomers(double threshold, Enums::MassType mass_type); //////////////// OPERATORS ///////////////////// bool isChemicallyEquivalent(const PolChemDef &other) const; bool operator==(const PolChemDef &other) const; bool operator!=(const PolChemDef &other) const; //////////////// ISOTOPIC DATA LOADING WRITING ///////////////////// static std::size_t loadIsotopicData(PolChemDefSPtr pol_chem_def_sp, const QString &file_path = QString()); static std::size_t writeIsotopicData(PolChemDefSPtr pol_chem_def_sp, const QString &file_path = QString()); //////////////// XML DATA LOADING WRITING ///////////////////// static bool renderXmlPolChemDefFile(PolChemDefSPtr pol_chem_def_sp); static bool renderXmlPolChemDefFileVersion1(PolChemDefSPtr pol_chem_def_sp); static bool renderXmlPolChemDefFileVersion2(PolChemDefSPtr pol_chem_def_sp); QString formatXmlDtd(); bool writeXmlFile(); //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// UTILS ///////////////////// protected: QString m_name; QString m_xmlDataFilePath; QString m_isotopicDataFilePath; Formula m_leftCap; Formula m_rightCap; int m_codeLength = -1; QString m_delimitedCodes; Ionizer m_ionizer; IsotopicDataSPtr msp_isotopicData = nullptr; std::vector m_modifs; std::vector m_monomers; std::vector m_crossLinkers; std::vector m_cleavageAgents; std::vector m_fragmentationPathways; std::vector m_polChemDefs; mutable bool m_isValid = false; }; } // namespace libXpertMassCore } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::PolChemDefSPtr); extern int polChemDefSPtrMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::PolChemDefCstSPtr); extern int polChemDefCstSPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/PolChemDefSpec.hpp000664 001750 001750 00000004335 15100504560 033041 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC PolChemDefSpec { public: PolChemDefSpec(const QString &name = QString()); ~PolChemDefSpec(); void setName(const QString &name); const QString &getName() const; void setFilePath(const QString &file_path); QString getFilePath() const; QString dirPath(); protected: // Name of the polymer definition(ie polymer type, 'protein'). QString m_name; // PolChemDef definition file(path relative to the polymer chemistry // definitions directory in the data directory, as found in the // catalogue files, like protein-1-letter/protein-1-letter.xml QString m_filePath; }; typedef std::shared_ptr PolChemDefSpecSPtr; typedef std::shared_ptr PolChemDefSpecCstSPtr; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Polymer.hpp000664 001750 001750 00000034343 15100504560 031711 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes #include #include /////////////////////// Qt includes #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/CrossLink.hpp" #include "MsXpS/libXpertMassCore/Sequence.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" #include "MsXpS/libXpertMassCore/CalcOptions.hpp" namespace MsXpS { namespace libXpertMassCore { const int POL_SEQ_FILE_FORMAT_VERSION = 1; class PolChemDef; typedef std::shared_ptr PolChemDefCstSPtr; typedef QSharedPointer PolChemDefQSPtr; typedef QSharedPointer PolChemDefCstQSPtr; class Polymer; typedef std::shared_ptr PolymerSPtr; typedef std::shared_ptr PolymerCstSPtr; typedef QSharedPointer PolymerQSPtr; typedef QSharedPointer PolymerCstQSPtr; class Ionizer; class CrossLink; class CrossLinkList; /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Polymer */ class DECLSPEC Polymer: public QObject, public QEnableSharedFromThis { friend class Ionizer; Q_OBJECT Q_PROPERTY(QString name WRITE setName READ getName) Q_PROPERTY(QString code WRITE setCode READ getCode) Q_PROPERTY(QString author WRITE setAuthor READ getAuthor) Q_PROPERTY(QString filePath WRITE setFilePath READ getFilePath) Q_PROPERTY(QString sequence WRITE setSequence READ getSequenceText) Q_PROPERTY(std::size_t size READ size) public: static PolymerQSPtr createSPtr(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &code = QString(), const QString &author = QString(), const QString &sequence_string = QString()); Polymer(const Polymer &other) = delete; Polymer &operator=(const Polymer &other) = delete; virtual ~Polymer(); //////////////// THE SHARED POINTER ///////////////////// PolymerCstQSPtr getCstSharedPtr(); PolymerQSPtr getSharedPtr(); //////////////// THE POLCHEMDEF ///////////////////// void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); const PolChemDefCstSPtr &getPolChemDefCstSPtr() const; //////////////// THE NAME ///////////////////// Q_INVOKABLE void setName(const QString &name); Q_INVOKABLE QString getName() const; //////////////// THE CODE ///////////////////// Q_INVOKABLE void setCode(const QString &code); Q_INVOKABLE QString getCode() const; //////////////// THE AUTHOR ///////////////////// Q_INVOKABLE void setAuthor(const QString &author); Q_INVOKABLE QString getAuthor() const; //////////////// THE FILE PATH ///////////////////// Q_INVOKABLE void setFilePath(const QString &file_path); Q_INVOKABLE QString getFilePath() const; Q_INVOKABLE void setDateTime(); Q_INVOKABLE void setDateTime(const QString &date_time); Q_INVOKABLE QString getDateTime() const; //////////////// THE SEQUENCE ///////////////////// Q_INVOKABLE void setSequence(const QString &sequence_string); void setSequence(const Sequence &sequence); const Sequence &getSequenceCstRef() const; Sequence &getSequenceRef(); Q_INVOKABLE QString getSequenceText() const; Q_INVOKABLE std::size_t size() const; // MONOMER MODIFICATIONS ///////////////////////////// std::size_t modifiedMonomerCount(const IndexRangeCollection &index_ranges) const; Q_INVOKABLE bool modifyMonomer(std::size_t index, const QString modif_name, bool override); Q_INVOKABLE bool hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const; // POLYMER MODIFICATIONS ///////////////////////////// Q_INVOKABLE void unmodify(Enums::PolymerEnd polymer_end); // LEFT END Q_INVOKABLE bool setLeftEndModifByName(const QString &name = QString()); bool setLeftEndModif(const Modif &modif); const Modif &getLeftEndModifCstRef() const; Modif &getLeftEndModifRef(); bool isLeftEndModified() const; // RIGHT END Q_INVOKABLE bool setRightEndModifByName(const QString &name = QString()); bool setRightEndModif(const Modif &); const Modif &getRightEndModifCstRef() const; Modif &getRightEndModifRef(); bool isRightEndModified() const; //////////////// THE CALCULATION OPTIONS ///////////////////// Q_INVOKABLE void setCalcOptions(const CalcOptions &calc_options); Q_INVOKABLE void setCalcOptions(const CalcOptions *calc_options); const CalcOptions &getCalcOptionsCstRef() const; const CalcOptions &getCalcOptionsRef(); Q_INVOKABLE CalcOptions *getCalcOptions(); // IONIZATION ///////////////////////////// Q_INVOKABLE void setIonizer(const Ionizer *ionizer_p); void setIonizer(const Ionizer &ionizer); const Ionizer &getIonizerCstRef() const; Ionizer &getIonizerRef(); Q_INVOKABLE Ionizer *getIonizer(); Q_INVOKABLE Enums::IonizationOutcome ionize(); Q_INVOKABLE Enums::IonizationOutcome ionize(const Ionizer &ionizer); Q_INVOKABLE Enums::IonizationOutcome deionize(); Q_INVOKABLE Enums::IonizationOutcome deionize(const Ionizer &ionizer); Q_INVOKABLE Enums::IonizationOutcome molecularMasses(double &mono, double &avg) const; // CROSS-LINKS ///////////////////////////// Q_INVOKABLE std::size_t crossLinkCount() const; const std::vector &getCrossLinksCstRef() const; std::vector &getCrossLinksRef(); bool crossLinkedMonomersIndicesInRange(std::size_t start_index, std::size_t stop_index, std::vector &indices, std::size_t &partials) const; bool crossLinksInRange( std::size_t start_index, std::size_t stop_index, std::vector &cross_links, std::size_t &partials) const; std::vector crossLinkIndicesInvolvingMonomer(MonomerSPtr monomer_sp) const; std::vector crossLinkIndicesInvolvingMonomer(MonomerCstRPtr monomer_crp) const; QString crossLink(CrossLinkSPtr cross_link_sp); bool uncrossLink(CrossLinkSPtr cross_link_sp); // MONOMER REMOVAL ///////////////////////////// std::size_t prepareMonomerRemoval(MonomerSPtr monomer_csp); bool removeMonomerAt(std::size_t index); // MASS CALCULATION FUNCTIONS ///////////////////////////// Q_INVOKABLE static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg, bool reset); Q_INVOKABLE static bool calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, const Ionizer &ionizer, double &mono, double &avg); Q_INVOKABLE bool calculateMasses(const CalcOptions &calc_options, bool reset); Q_INVOKABLE bool calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer); Q_INVOKABLE bool calculateMasses(const CalcOptions *calc_options_p, const Ionizer *ionizer_p); Q_INVOKABLE bool calculateMasses(); bool accountMasses(const CalcOptions &calc_options); static bool accountMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg); bool accountCappingMasses(Enums::CapType cap_type, int times = 1); static bool accountCappingMasses(const Polymer *polymer_p, Enums::CapType cap_type, double &mono, double &avg, int times = 1); void accountEndModifMasses(Enums::ChemicalEntity polymer_chem_entities); static void accountEndModifMasses(const Polymer *polymer_p, Enums::ChemicalEntity polymer_chem_entities, double &mono, double &avg); Q_INVOKABLE double getMass(Enums::MassType mass_type) const; Q_INVOKABLE double &getMassRef(Enums::MassType mass_type); // ELEMENTAL COMPOSITION //////////////////////////// Q_INVOKABLE QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options) const; Q_INVOKABLE QString elementalComposition(const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options, const Ionizer &ionizer) const; Q_INVOKABLE QString elementalComposition(const CalcOptions &calc_options) const; Q_INVOKABLE QString elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const; // XML DATA RENDERING AND WRITING /////////////////////////////////// bool renderXmlCodesElement(const QDomElement &element); static QString xmlPolymerFileGetPolChemDefName(const QString &file_path); bool renderXmlPolymerFile(const QString &file_path = QString("")); bool renderXmlPolymerModifElement(const QDomElement &, int); bool renderXmlCrossLinksElement(const QDomElement &, int); QString formatXmlDtd(); QString formatXmlPolSeqElement(int, const QString & = QString(" ")); QString formatXmlCrossLinksElement(int, const QString & = QString(" ")); bool writeXmlFile(); // VALIDATIONS //////////////////////////// bool validate(ErrorList *error_list_p) const; Q_INVOKABLE bool isValid() const; // UTILITIES //////////////////////////// QByteArray md5Sum(int hash_data_specifs) const; QString storeCrossLink(CrossLinkSPtr cross_link_sp); bool hasCrossLink(const CrossLinkSPtr &cross_links_sp) const; bool hasUuid(const CrossLinkSPtr &cross_links_sp) const; CrossLinkSPtr getCrossLinkFromUuid(const QString &uuid) const; QString getUuidForCrossLink(const CrossLinkSPtr &cross_link_sp) const; MonomerCstSPtr getCrossLinkedMonomerCstSPtrFromUuid(const QString &uuid) const; std::vector getAllCrossLinkUuids() const; Q_INVOKABLE QString toString() const; Q_INVOKABLE void clear(); static void registerJsConstructor(QJSEngine *engine); signals: void polymerDestroyedSignal(Polymer *polymer_p); void crossLinkChangedSignal(Polymer *polymer_p); void crossLinksPartiallyEncompassedSignal(int partial_cross_links_count) const; protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; QString m_name; QString m_code; QString m_author; QString m_filePath; QDateTime m_dateTime; Sequence m_sequence; Modif m_leftEndModif; Modif m_rightEndModif; // Always heap-allocated during construction CalcOptions *mp_calcOptions = nullptr; // Always heap-allocated during construction Ionizer *mp_ionizer = nullptr; std::vector m_crossLinks; // We need in code that uses this class, to constantly keep at hand // the CrossLink instances. For example, // we need to store the CrossLink pointers as strings in QListWidget items // (Qt:UserRole). // We thus make use of the QUuid class to craft a Uuid string that // we associate to the CrossLink weak pointer that in turn relates // to the CrossLink shared pointer. // Note that we create CrossLink instances as SPtr (non-const) but we // provide functions to access them as const. // When a CrossLink is created, it SPtr is referenced along a newly-created // QUuid string and store here along with a WeakPtr of the SPtr. std::vector m_uuidCrossLinkPairs; double m_mono; double m_avg; mutable bool m_isValid = false; private: Polymer(PolChemDefCstSPtr pol_chem_def_csp = nullptr, const QString &name = QString(), const QString &code = QString(), const QString &author = QString(), const QString &sequence_string = QString()); bool removeCrossLink(CrossLinkSPtr crossLink_sp); void removeCrossLink(const QString &uuid); void cleanupCrossLinks(); }; /* END CLASS JS REFERENCE namespace: MsXpS::libXpertMassCore class name: Polymer */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Polymer) } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::Polymer); extern int polymerMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::PolymerSPtr); extern int polymerSPtrMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::PolymerCstQSPtr); extern int PolymerCstQSPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Prop.hpp000664 001750 001750 00000011615 15100504560 031177 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" namespace MsXpS { namespace libXpertMassCore { class PolChemDef; typedef std::shared_ptr PolChemDefCstSPtr; //////////////////////// Prop //////////////////////// //////////////////////// Prop //////////////////////// class DECLSPEC Prop { public: Prop(); Prop(const QString &name); Prop(const Prop &other); // Destructor. virtual ~Prop(); void setName(QString &); const QString &name(); void setData(void *data_p); virtual void *data() const; virtual void deleteData(); virtual Prop &operator=(const Prop &other); virtual Prop *cloneOut() const = 0; virtual bool renderXmlElement(const QDomElement &, int = 1); virtual QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); protected: QString m_name = "NOT_SET"; void *mpa_data = nullptr; }; class DECLSPEC StringProp : public Prop { public: // Constructors StringProp(const QString &name = QString(), const QString &data = QString()); StringProp(const StringProp &other); // Destructor. virtual ~StringProp(); virtual void deleteData(); using Prop::operator=; virtual StringProp &operator=(const StringProp &other); virtual StringProp *cloneOut() const; virtual bool renderXmlElement(const QDomElement &, int = 1); virtual QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); }; //////////////////////// IntProp //////////////////////// //////////////////////// IntProp //////////////////////// class DECLSPEC IntProp : public Prop { public: // constructors IntProp(const QString & = QString(), int = 0); IntProp(const IntProp &other); // Destructor. virtual ~IntProp(); virtual void deleteData(); using Prop::operator=; virtual IntProp &operator=(const IntProp &other); virtual IntProp *cloneOut() const; virtual bool renderXmlElement(const QDomElement &, int = 1); virtual QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); }; //////////////////////// DoubleProp //////////////////////// //////////////////////// DoubleProp //////////////////////// class DECLSPEC DoubleProp : public Prop { public: DoubleProp(const QString & = QString(), double = 0); DoubleProp(const DoubleProp &other); virtual ~DoubleProp(); virtual void deleteData(); using Prop::operator=; virtual DoubleProp &operator=(const DoubleProp &other); virtual DoubleProp *cloneOut() const; virtual bool renderXmlElement(const QDomElement &, int = 1); virtual QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); }; /////////////////// NoDeletePointerProp /////////////////// /////////////////// NoDeletePointerProp /////////////////// class DECLSPEC NoDeletePointerProp : public Prop { public: NoDeletePointerProp(const QString & = QString(), void * = 0); NoDeletePointerProp(const NoDeletePointerProp &other); virtual ~NoDeletePointerProp(); virtual void deleteData(); using Prop::operator=; virtual NoDeletePointerProp &operator=(const NoDeletePointerProp &other); virtual NoDeletePointerProp *cloneOut() const; virtual bool renderXmlElement(const QDomElement &, int = 1); virtual QString formatXmlElement(int offset, const QString &indent = Utils::xmlIndentationToken); }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/PropListHolder.hpp000664 001750 001750 00000004507 15100504560 033173 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/Prop.hpp" namespace MsXpS { namespace libXpertMassCore { //! The PropList class provides a list of allocated Prop instances. /*! PropList provides a list of allocated Prop instances that it owns. This class is used to derive classes needing to store dynamically allocated Prop objects without knowing with anticipation how many of such objects are going to be used. */ class DECLSPEC PropListHolder { public: PropListHolder(); PropListHolder(const PropListHolder &other); virtual ~PropListHolder(); virtual PropListHolder &operator=(const PropListHolder &other); const QList &propList() const; QList &propList(); Prop *prop(const QString &, int * = 0); int propIndex(const QString &, Prop **prop_pp = nullptr); int propListSize() const; bool appendProp(Prop *); bool removeProp(Prop *); bool removeProp(const QString &); protected: QList m_propList; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Sequence.hpp000664 001750 001750 00000014102 15100504560 032021 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once ////////////////////////////// Stdlib includes #include /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" namespace MsXpS { namespace libXpertMassCore { typedef std::vector::iterator SequenceIterator; typedef std::vector::const_iterator SequenceCstIterator; class DECLSPEC Sequence { friend class Polymer; friend class Oligomer; public: Sequence(); Sequence(PolChemDefCstSPtr pol_chem_def_csp, const QString &sequence_text); Sequence(const Sequence &other); virtual ~Sequence(); //////////////// POLYMER CHEMISTRY DEFINTIION ///////////////////// void setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp); PolChemDefCstSPtr getPolChemDef() const; //////////////// MONOMERS TEXT / INSTANCES ///////////////////// int setSequence(const QString &sequence, std::vector &failing_indices); int appendSequence(const QString &sequence, std::vector &failing_indices); QString getSequence() const; QString getSequence(std::size_t start_index, std::size_t stop_index, bool with_modifs = false) const; QString getSequence(const IndexRangeCollection &index_ranges, bool with_modifs = false, bool delimited_regions = false) const; int makeMonomers(const QString &sequence_text, bool reset, std::vector &failing_indices); const std::vector &getMonomersCstRef() const; std::vector &getMonomersRef(); //////////////// MONOMER ACCESSING FUNCTIONS ///////////////////// MonomerCstRPtr getMonomerCstRPtrAt(std::size_t index) const; MonomerRPtr getMonomerRPtrAt(std::size_t index); MonomerSPtr getMonomerCstSPtrAt(std::size_t index) const; MonomerSPtr getMonomerSPtrAt(std::size_t index); std::size_t monomerIndex(MonomerSPtr monomer_sp, bool &ok) const; std::size_t monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const; std::size_t monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const; std::size_t nextCode(const QString &sequence, QString &code, std::size_t &index, QString &err); //////////////// MONOMER HANDLING FUNCTIONS ///////////////////// QString insertMonomerAt(const Monomer &monomer, std::size_t index); bool removeMonomerAt(std::size_t index); bool modifyMonomer(std::size_t index, const QString modif_name, bool override); bool hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const; std::vector modifiedMonomerIndices(std::size_t left_index, std::size_t right_index) const; //////////////// SEQUENCE SEARCH FUNCTIONS ///////////////////// int findForwardMotif(const Sequence &sequence_motif, std::size_t &index) const; //////////////// DIAGNOSTICS FUNCTIONS ///////////////////// std::size_t size() const; bool isInBound(std::size_t index); //////////////// OPERATORS ///////////////////// Sequence &operator=(const Sequence &other); bool operator==(const Sequence &other); bool operator!=(const Sequence &other); //////////////// VALIDATIONS ///////////////////// bool validate(ErrorList *error_list_p) const; bool isValid() const; //////////////// UTILS ///////////////////// QString unspacifySequence(const QString &monomer_text); QString storeMonomer(const MonomerSPtr &monomer_sp); QString storeMonomer(const MonomerSPtr &monomer_sp, std::size_t index); bool hasMonomer(const MonomerSPtr &monomer_sp) const; bool hasUuid(const MonomerSPtr &monomer_sp) const; MonomerSPtr getMonomerForUuid(const QString &uuid) const; QString getUuidForMonomer(const MonomerSPtr &monomer_sp) const; std::vector getAllMonomerUuids() const; void cleanupMonomers(); quint16 checksum(int = -1, int = -1, bool = false) const; void clear(); protected: PolChemDefCstSPtr mcsp_polChemDef = nullptr; std::vector m_monomers; // We need in code that uses this class, to constantly keep at hand // the Monomer instances that are involved in CrossLink_s or that need // to be reported as containing Modif instances. For example, // we need to store the Monomer pointers as strings in QListWidget items // (Qt:UserRole). // We thus make use of the QUuid class to craft a Uuid string that // we associate to the Monomer weak pointer that in turn relates // to the Monomer shared pointer. std::vector m_uuidMonomerPairs; mutable bool m_isValid = false; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Tolerance.hpp000664 001750 001750 00000006111 15100504560 032166 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { class DECLSPEC Tolerance : public QObject { Q_OBJECT Q_PROPERTY( double nominal READ getNominal WRITE setNominal NOTIFY nominalChangedSignal) Q_PROPERTY(Type type READ getType WRITE setType NOTIFY typeChangedSignal) public: enum class Type { NOT_SET, DALTON, RES, PPM, LAST }; Q_ENUM(Type) std::map m_typeToStringMap = { {Type::DALTON, "Dalton"}, {Type::RES, "Res"}, {Type::PPM, "Ppm"}}; Q_INVOKABLE explicit Tolerance(QObject *parent = nullptr); Q_INVOKABLE explicit Tolerance(Type tolerance_type, double nominal, QObject *parent = nullptr); Q_INVOKABLE void setNominal(double nominal); Q_INVOKABLE double getNominal() const; Q_INVOKABLE void setType(Type type); Q_INVOKABLE void setType(const QString &text); Q_INVOKABLE Type getType() const; Q_INVOKABLE Type getType(const QString &text) const; Q_INVOKABLE QString getTypeAsString() const; Q_INVOKABLE void initialize(double nominal, Tolerance::Type type); Q_INVOKABLE void initialize(const Tolerance &tolerance); Q_INVOKABLE Tolerance *clone(QObject *parent = nullptr); Q_INVOKABLE double calculateWidth(double reason) const; Q_INVOKABLE QString toString() const; signals: void nominalChangedSignal(); void typeChangedSignal(); void toleranceChangedSignal(std::pair); void widthChangedSignal(double) const; private: double m_nominal = 40000; Type m_type = Type::RES; }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/Utils.hpp000664 001750 001750 00000016435 15100504560 031364 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once ////////////////////////////// Stdlib includes #include ////////////////////////////// Qt includes #include #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" /* Bitwise stuff (from StackOverflow) It is sometimes worth using an enum to name the bits: enum ThingFlags = { ThingMask = 0x0000, ThingFlag0 = 1 << 0, ThingFlag1 = 1 << 1, ThingError = 1 << 8, } Then use the names later on. I.e. write thingstate |= ThingFlag1; thingstate &= ~ThingFlag0; if (thing & ThingError) {...} to set, clear and test. This way you hide the magic numbers from the rest of your code. */ namespace MsXpS { namespace libXpertMassCore { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Utils * * BEGIN DESCRIPTION * This class can be used in JS in two ways: * 1. By direct use of the 'utils' singleton * and directly calling the invokable functions like so: * utils.unspacify("this text has spaces") * 2. By instantiating an object like so: * var the_utils = new Utils() * and later calling the functions as methods of the * object like so: * the_utils.unspacify("this text has spaces") * END DESCRIPTION */ class DECLSPEC Utils: public QObject { Q_OBJECT /*$ JS PROP REF Defines the string used for XML indentation formatting*/ Q_PROPERTY(QString xmlIndentationToken MEMBER xmlIndentationToken) public: /*$ JS INVOK REF Creates a new instance of Utils */ Q_INVOKABLE explicit Utils(QObject *parent = nullptr); virtual ~Utils(); // This regexp extracts a formula from a larger action formula containing one // or more subformulas. For example it would extract from the // "+C12.1-H0.9Na2-N0.5" formula the following subformulas: +C12.1 and -H0.9 // and Na2 and -N0.5. This regular expression should be used with the // globalMatch() feature of QRegularExpression. static QRegularExpression subFormulaRegExp; static QRegularExpression xyFormatMassDataRegExp; static QRegularExpression endOfLineRegExp; static QString xmlIndentationToken; static void messageOutputFormat(QtMsgType type, const QMessageLogContext &context, const QString &msg); void configureDebugMessagesFormat(); /*$ JS INVOK REF Given the list of double values sets a number of statistical * values*/ Q_INVOKABLE static void doubleListStatistics(QList list, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &greatest, double &median); static void doubleVectorStatistics(std::vector &vector, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &greatest, double &median); /*$ JS INVOK REF Tells if two double values can be told apart with the * precision of the computer.*/ Q_INVOKABLE static bool almostEqual(double value1, double value2, int decimalPlaces = 10); QTextStream &qStdOut(); /*$ JS INVOK REF Remove any space anywhere in the text string.*/ Q_INVOKABLE static QString unspacify(const QString &text); /*$ JS INVOK REF Returns the binary representation of the value.*/ Q_INVOKABLE static QString binaryRepresentation(int value); Q_INVOKABLE static QString elideText(const QString &text, int charsLeft = 4, int charsRight = 4, const QString &delimitor = "..."); Q_INVOKABLE static QString stanzify(const QString &text, int width); Q_INVOKABLE static QString stanzifyParagraphs(const QString &text, int width); Q_INVOKABLE static int countZeroDecimals(double value); /*$ JS INVOK REF Returns the mass delta corresponding to ppm of value.*/ Q_INVOKABLE static double ppm(double value, double ppm); Q_INVOKABLE static double addPpm(double value, double ppm); Q_INVOKABLE static double removePpm(double value, double ppm); Q_INVOKABLE static double res(double value, double res); Q_INVOKABLE static double addRes(double value, double res); Q_INVOKABLE static double removeRes(double value, double res); template static QString pointerAsString(T *ptr); template static T *stringAsPointer(const QString &str); static QString craftConfigSettingsFilePath(const QString &module_name); static int testDiffBetweenTwoTextFiles(const QString &file_path_1, const QString &file_path_2); Q_INVOKABLE static QString joinErrorList(const ErrorList &error_list, const QString &separator = "\n"); #ifdef Q_OS_LINUX static bool isProcessRunningInLinux(const QString &process_name); #endif #ifdef Q_OS_WIN static bool isProcessRunningInWindows(const QString &process_name); #endif Q_INVOKABLE static bool isProgramRunning(const QString &program_name); static void registerJsConstructor(QJSEngine *engine); }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassCore * class name: Utils */ } // namespace libXpertMassCore MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Utils) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/XpertMassCoreJavaScript.hpp000664 001750 001750 00000001035 15100504560 035000 0ustar00rusconirusconi000000 000000 #pragma once #include #include "MsXpS/export-import-config.h" #include "MsXpS/libXpertMassCore/globals.hpp" namespace MsXpS { namespace libXpertMassCore { DECLSPEC void registerJsQmlTypes(const char *uri = "libXpertMassCore", int versionMajor = 1, int versionMinor = 0); DECLSPEC void registerEnumsToQJSEngine(QJSEngine *engine); DECLSPEC void registerGlobalsToQJSEngine(QJSEngine *engine); } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/globals.hpp000664 001750 001750 00000021220 15100504560 031673 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once ////////////////////////////// Stdlib includes #include #include ////////////////////////////// Qt includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" /* Bitwise stuff (from StackOverflow) It is sometimes worth using an enum to name the bits: enum ThingFlags = { ThingMask = 0x0000, ThingFlag0 = 1 << 0, ThingFlag1 = 1 << 1, ThingError = 1 << 8, } Then use the names later on. I.e. write thingstate |= ThingFlag1; thingstate &= ~ThingFlag0; if (thing & ThingError) {...} to set, clear and test. This way you hide the magic numbers from the rest of your code. */ namespace MsXpS { namespace libXpertMassCore { // Macro to fake qFatal() << streaming in Qt 6.4 #if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) // If Qt < 6.8 #define qFatalStream() \ for(QString fatalMsg; fatalMsg.isEmpty(); \ qFatal("%s", fatalMsg.toUtf8().constData())) \ QDebug(&fatalMsg).noquote().nospace() #else #define qFatalStream() qFatal() #endif void myMessageOutputFormat(QtMsgType type, const QMessageLogContext &context, const QString &msg); void configureDebugMessagesFormat(); /////////////////////////// namespace Enums //////////////////////////// /////////////////////////// namespace Enums //////////////////////////// /////////////////////////// namespace Enums //////////////////////////// namespace Enums { DECLSPEC Q_NAMESPACE enum class LocationType { INDEX = 0, POSITION = 1, }; Q_ENUM_NS(LocationType) enum class MassType { MONO = 1 << 0, //!< Monoisotopic mass AVG = 1 << 1, //!< Average mass BOTH = (MONO | AVG) //!< Both masses }; Q_ENUM_NS(MassType) // Overload bitwise AND operator inline MassType operator&(MassType lhs, MassType rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } enum class PolChemDefEntityStatus { POL_CHEM_DEF_NOT_AVAILABLE = 0, ENTITY_NOT_KNOWN, ENTITY_KNOWN }; Q_ENUM_NS(PolChemDefEntityStatus) enum class MassToleranceType { NONE = 0, //!< parts per million PPM, //!< m/z ratio MZ, //!< atomic mass units AMU, //!< resolution, that is [1 / m/z] RES, LAST }; Q_ENUM_NS(MassToleranceType) enum class PolymerEnd { NONE = 0 << 1, LEFT = 1 << 1, RIGHT = 2 << 1, BOTH = (LEFT | RIGHT) }; Q_ENUM_NS(PolymerEnd) // Overload bitwise AND operator inline PolymerEnd operator&(PolymerEnd lhs, PolymerEnd rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } enum IonizationOutcome { FAILED = 0, UNCHANGED = 1, IONIZED = 2, DEIONIZED = 3, }; Q_ENUM_NS(IonizationOutcome) enum class SelectionType { RESIDUAL_CHAINS, OLIGOMERS }; Q_ENUM_NS(SelectionType) enum class CapType { NONE = 0 << 1, LEFT = 1 << 1, RIGHT = 2 << 1, BOTH = (LEFT | RIGHT) }; Q_ENUM_NS(CapType) // Overload bitwise AND operator inline CapType operator&(CapType lhs, CapType rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } //! Chemical entities. enum class ChemicalEntity { NONE = 0, MONOMER = 1 << 0, MODIF = 1 << 1, CROSS_LINKER = 1 << 2, MODIF_AND_CROSS_LINKER = (MODIF | CROSS_LINKER), CROSS_LINK = 1 << 3, LEFT_END_MODIF = 1 << 4, FORCE_LEFT_END_MODIF = 1 << 5, RIGHT_END_MODIF = 1 << 6, FORCE_RIGHT_END_MODIF = 1 << 7, BOTH_END_MODIFS = (LEFT_END_MODIF | RIGHT_END_MODIF), FORCE_BOTH_END_MODIFS = (FORCE_LEFT_END_MODIF | FORCE_RIGHT_END_MODIF) }; Q_ENUM_NS(ChemicalEntity) // Overload bitwise AND operator inline Enums::ChemicalEntity operator&(Enums::ChemicalEntity lhs, Enums::ChemicalEntity rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } enum class CrossLinkEncompassed { NOT = 0, PARTIALLY = 1, FULLY = 2, }; Q_ENUM_NS(CrossLinkEncompassed) enum class HashAccountData { NOTHING = 0x0, SEQUENCE = 1 << 0, MONOMER_MODIF = 1 << 1, POLYMER_MODIF = 1 << 2 }; Q_ENUM_NS(HashAccountData) enum class CleavageAction { NOT_SET = -1, NO_CLEAVE = 0, CLEAVE = 1, }; Q_ENUM_NS(CleavageAction) enum class FragEnd { NE = 0x0, LE = 1 << 0, RE = 1 << 1, BE = (LE | RE), }; Q_ENUM_NS(FragEnd) // Overload bitwise AND operator inline FragEnd operator&(FragEnd lhs, FragEnd rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } enum class ChemicalGroupTrapped { NEVER = 0x0, LEFT = 1 << 0, RIGHT = 1 << 1, }; Q_ENUM_NS(ChemicalGroupTrapped) // Overload bitwise AND operator inline ChemicalGroupTrapped operator&(ChemicalGroupTrapped lhs, ChemicalGroupTrapped rhs) { return static_cast( static_cast>(lhs) & static_cast>(rhs)); } enum class ChemicalGroupFate { LOST = 0, PRESERVED = 1, }; Q_ENUM_NS(ChemicalGroupFate) enum class MassPeakShapeType { NOT_SET = 0, GAUSSIAN, LORENTZIAN, }; Q_ENUM_NS(MassPeakShapeType) enum class MassPeakWidthLogic { NOT_SET = 0, FWHM, RESOLUTION }; Q_ENUM_NS(MassPeakWidthLogic) enum class MassDataType { NOT_SET = 0, MASS_SPECTRUM, DRIFT_SPECTRUM, TIC_CHROMATOGRAM, XIC_CHROMATOGRAM, MZ_RT_COLORMAP, DT_RT_COLORMAP, DT_MZ_COLORMAP, LAST, }; Q_ENUM_NS(MassDataType) } // namespace Enums extern DECLSPEC std::map locationTypeMap; extern DECLSPEC std::map massTypeMap; extern DECLSPEC std::map massToleranceTypeMap; extern DECLSPEC std::map polymerEndMap; extern DECLSPEC std::map ionizationOutcomeMap; extern DECLSPEC std::map selectionTypeMap; extern DECLSPEC std::map capTypeMap; extern DECLSPEC std::map chemicalEntityMap; extern DECLSPEC std::map crossLinkEncompassedMap; extern DECLSPEC std::map CleavageActionMap; extern DECLSPEC std::map fragEndMap; extern DECLSPEC std::map chemicalGroupTrappedMap; extern DECLSPEC std::map chemicalGroupFateMap; extern DECLSPEC std::map massPeakShapeTypeMap; extern DECLSPEC std::map massPeakWidthLogicMap; extern DECLSPEC std::map massDataTypeMap; extern DECLSPEC int ATOM_DEC_PLACES; extern DECLSPEC int OLIGOMER_DEC_PLACES; extern DECLSPEC int POLYMER_DEC_PLACES; extern DECLSPEC int PKA_PH_PI_DEC_PLACES; typedef QStringList ErrorList; } // namespace libXpertMassCore } // namespace MsXpS Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::ErrorList) extern int errorListMetaTypeId; Q_DECLARE_METATYPE(MsXpS::libXpertMassCore::ErrorList *); extern int errorListPtrMetaTypeId; libxpertmass-1.4.0/source/XpertMassCore/includes/MsXpS/libXpertMassCore/jsclassregistrar.h000664 001750 001750 00000006614 15100504560 033307 0ustar00rusconirusconi000000 000000 #pragma once #include #include #include #include #include "MsXpS/export-import-config.h" namespace MsXpS { // This macro is fundamental for the registration of C++ QObject-derived // classes that we want to expose to JavaScript (QJSEngine/QQmlEngine). // This macros is added at the end of the class declaration in the // corresponding namespace (MsXps::libXpertMass or MsXpS::libXpertMassGui, // in the header file) in the form: // MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Isotope) // // This macro uses the getNameSpaceClassNameJsConstructorRegistrarMap() // function in the pappso namespace which return a map of maps: // The inner map maps the namespace with the class_name // The out map maps the inner map to the class' static member function // that performs the actual registration of the class to the QJSEngine // that is passed as parameter. What the // static void registerJsConstructor(QJSEngine *engine); // function does is simply to register a constructor in the javascript // global object that allows the JavaScript developer to call a constructor // like so: // var isotope = new Isotope() #define STRINGIFY_NS(ns) #ns #define MSXPS_REGISTER_JS_CLASS(NS_IDENT, CLASS_NAME) \ struct JsRegHelper_##CLASS_NAME \ { \ JsRegHelper_##CLASS_NAME() \ { \ MsXpS::getNameSpaceClassNameJsConstructorRegistrarMap().insert( \ {{QStringLiteral(STRINGIFY_NS(NS_IDENT)), QStringLiteral(#CLASS_NAME)},\ [](QJSEngine *engine) { \ NS_IDENT::CLASS_NAME::registerJsConstructor(engine); \ }}); \ } \ }; \ static JsRegHelper_##CLASS_NAME jsRegHelperInstance_##CLASS_NAME; // Key type: pair of namespace and class name using NamespaceClassnamePairAsKey = std::pair; using RegisterFunc = std::function; // Hash functor for NsClassKey struct NamespaceClassnameAsKeyHash { std::size_t operator()(const NamespaceClassnamePairAsKey &key) const noexcept { // Combine the hashes of the two QStrings return qHash(key.first) ^ (qHash(key.second) << 1); } }; // Equality functor (needed because we use QString) struct AreNamespaceClassnamePairsEqual { bool operator()(const NamespaceClassnamePairAsKey &a, const NamespaceClassnamePairAsKey &b) const noexcept { return a.first == b.first && a.second == b.second; } }; DECLSPEC std:: unordered_map & getNameSpaceClassNameJsConstructorRegistrarMap(); DECLSPEC void registerJsConstructorForNameSpaceClassNameInRegistrarMap(const QString &name_space, const QString &class_name, QJSEngine *engine); DECLSPEC void registerJsConstructorForEachClassInRegistrarMap(QJSEngine *engine); }// namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/000775 001750 001750 00000000000 15100507137 022266 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassCore/src/CalcOptions.cpp000664 001750 001750 00000033615 15100504560 025215 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CalcOptions.hpp" int calcOptionsMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::CalcOptions"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CalcOptions \inmodule libXpertMassCore \ingroup XpertMassMassCoreCalculations \inheaderfile CalcOptions.hpp \brief The CalcOptions class provides the specifications that configure the way masses are calculated for \l{Oligomer}s, \l{Polymer}s and product ions. */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_deepCalculation \brief Tells if the calculations must involve the recalculation of all the masses of the \l{Monomer}s in the sequence. This is typically needed when calculating masses involving modified Monomer instances, whereby each Monomer needs to recompute its mass taking into account the Modif instances attached to it. */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_massType \brief The mass type to compute, monoisotopic or average. \sa Enums::MassType */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_capType \brief The cap type, left or right (or both), to account for in the calculations. \sa Enums::CapType */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_monomerEntities \brief The \l Monomer entities to account for in the calculations. \sa Enums::ChemicalEntity */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_polymerEntities \brief The \l Polymer entities to account for in the calculations. \sa Enums::ChemicalEntity */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::m_selectionType \brief The manner the monomers need to be accounted for: as residual chains or as finished-polymerization state sequences. The calculations might consider only the residual chain. In that case, only the mass of the monomers is considered and then the polymer is not in its finished polymerization state. If that latter state is required, then, the residual chain must be capped with both the left and the right end caps. \sa MsXpS::libXpertMassCore::Enums::SelectionType */ /*! \variable MsXpS::libXpertMassCore::CalcOptions::mp_indexRangeCollection \brief The IndexRangeCollection container that contains all the IndexRange instances that describes what regions of the \l Polymer need to be accounted for in the calculation. Most generally, there will be only one IndexRange, but there might be situations in which multiple regions might be selected. */ /*! \brief Constructs an empty CalcOptions instance. */ CalcOptions::CalcOptions(QObject *parent) : QObject(parent), mp_indexRangeCollection(new IndexRangeCollection(this)) { } /*! \brief Constructs a CalcOptions instance. All the members are initialized using the parameters: \list \li \a deepCalculation: m_deepCalculation (defaults to false) \li \a mass_type: m_massType (defaults to Enums::MassType::BOTH) \li \a capping: m_capType (defaults to Enums::CapType::BOTH) \li \a monomer_entities The monomer entities to be accounted for in the calculations (defaults to Enums::ChemicalEntity::NONE) \li \a polymer_entities: The polymer entities to be accounted for in the calculations (defaults to Enums::ChemicalEntity::NONE) \endlist The selection type is set by default to Oligomer (not residual chain). */ CalcOptions::CalcOptions(bool deepCalculation, Enums::MassType mass_type, Enums::CapType capping, Enums::ChemicalEntity monomer_entities, Enums::ChemicalEntity polymer_entities, QObject *parent) : QObject(parent), m_deepCalculation(deepCalculation), m_massType(mass_type), m_capType(capping), m_monomerEntities(monomer_entities), m_polymerEntities(polymer_entities), mp_indexRangeCollection(new IndexRangeCollection(this)) { } /*! \brief Construct a CalcOptions instance as a copy of \a other. */ CalcOptions::CalcOptions(const CalcOptions &other, QObject *parent) : QObject(parent), m_deepCalculation(other.m_deepCalculation), m_massType(other.m_massType), m_capType(other.m_capType), m_monomerEntities(other.m_monomerEntities), m_polymerEntities(other.m_polymerEntities), m_selectionType(other.m_selectionType), mp_indexRangeCollection( new IndexRangeCollection(*other.mp_indexRangeCollection, this)) { qDebug() << "In pseudo copy constructor, other IndexRangeCollection indices as text:" << other.getIndexRangeCollectionCstRef().indicesAsText(); // mp_indexRangeCollection = new IndexRangeCollection(this); // // foreach(const IndexRange *item, // other.getIndexRangeCollectionCstRef().getRangesCstRef()) // mp_indexRangeCollection->appendIndexRange(*item); qDebug().noquote() << "After pseudo copy constructor, with other calc options:" << other.toString() << "\n this calc options:" << this->toString(); } /*! \brief Returns a reference to this CalcOptions after having initialized it using \a other. */ CalcOptions & CalcOptions::initialize(const CalcOptions &other) { if(this == &other) return *this; m_deepCalculation = other.m_deepCalculation; m_massType = other.m_massType; m_capType = other.m_capType; m_monomerEntities = other.m_monomerEntities; m_polymerEntities = other.m_polymerEntities; m_selectionType = other.m_selectionType; mp_indexRangeCollection->setIndexRanges( other.mp_indexRangeCollection->getRangesCstRef()); return *this; } /*! \brief Returns an newly allocated CalcOptions instance with parent set to \a parent. */ CalcOptions * CalcOptions::clone(QObject *parent) { CalcOptions *copy_p = new CalcOptions(parent); copy_p->initialize(*this); return copy_p; } /*! \ b*rief Returns an newly allocated CalcOptions instance initialized using \a other and with parent set to \a parent. */ CalcOptions * CalcOptions::clone(const CalcOptions &other, QObject *parent) { CalcOptions *copy_p = new CalcOptions(parent); copy_p->initialize(other); return copy_p; } /*! \brief Destructs this CalcOptions object. Clear the member coordinateList. */ CalcOptions::~CalcOptions() { } /*! \brief Sets to \a deep the configuration defining if the mass calculations must involve the recalculation of the masses of all the monomers. The deep calculation is typically requested when computing masses involving Monomer instance that might be modified with Modif instances. \sa m_deepCalculation */ void CalcOptions::setDeepCalculation(bool deep) { m_deepCalculation = deep; } /*! \brief Returns if the calculation should be deep. \sa m_deepCalculation */ bool CalcOptions::isDeepCalculation() const { return m_deepCalculation; } /*! \brief Copies \a index_range to the member IndexRangeCollection instance. \note The IndexRangeCollection instance is first emptied, making index_range essentially replacing its contents with a copy of \a index_range. */ void CalcOptions::setIndexRange(const IndexRange &index_range) { mp_indexRangeCollection->setIndexRange(index_range); } /*! \brief Creates an IndexRange instance with \a index_start and \a index_end and sets it as the sole member of the member IndexRangeCollection instance. */ void CalcOptions::setIndexRange(qsizetype index_start, qsizetype index_end) { setIndexRange(IndexRange(index_start, index_end)); } /*! \brief Allocates a copy of each IndexRange instance in the \a index_ranges container and adds it to the member IndexRangeCollection instance. \note The IndexRangeCollection instance is first emptied, essentially replacing its contents with a copy of those in \a index_ranges. */ void CalcOptions::setIndexRanges(const IndexRangeCollection &index_ranges) { mp_indexRangeCollection->setIndexRanges(index_ranges); } /*! \brief Returns a const reference to the member IndexRangeCollection instance. */ const IndexRangeCollection & CalcOptions::getIndexRangeCollectionCstRef() const { return *mp_indexRangeCollection; } /*! \brief Returns a reference to the member IndexRangeCollection instance. */ IndexRangeCollection & CalcOptions::getIndexRangeCollectionRef() { return *mp_indexRangeCollection; } /*! \brief Returns a reference to the member IndexRangeCollection instance. */ IndexRangeCollection * CalcOptions::getIndexRangeCollection() { return mp_indexRangeCollection; } /*! \brief Sets the mass type to \a mass_type. \sa m_massType */ void CalcOptions::setMassType(Enums::MassType mass_type) { m_massType = mass_type; } /*! \brief Returns the mass type. \sa m_massType */ Enums::MassType CalcOptions::getMassType() const { return m_massType; } /*! \brief Set the selection type to \a selection_type. \sa m_selectionType */ void CalcOptions::setSelectionType(Enums::SelectionType selection_type) { m_selectionType = selection_type; } /*! \brief Returns the selection type. \sa m_selectionType */ Enums::SelectionType CalcOptions::getSelectionType() const { return m_selectionType; } /*! \brief Sets the cap type to \a cap_type. \sa m_capType */ void CalcOptions::setCapType(Enums::CapType cap_type) { m_capType = cap_type; } /*! \brief Returns the cap type. \sa m_capType */ Enums::CapType CalcOptions::getCapType() const { return m_capType; } /*! \brief Sets the monomer entities to \a monomer_chem_ent. \sa m_monomerEntities */ void CalcOptions::setMonomerEntities(Enums::ChemicalEntity monomer_chem_ent) { m_monomerEntities = monomer_chem_ent; } /*! \brief Returns the monomer entities. \sa m_monomerEntities */ Enums::ChemicalEntity CalcOptions::getMonomerEntities() const { return m_monomerEntities; } /*! \brief Sets the polymer entities to \a polymer_chem_ent. \sa m_polymerEntities */ void CalcOptions::setPolymerEntities(Enums::ChemicalEntity polymer_chem_ent) { m_polymerEntities = polymer_chem_ent; } /*! \brief Returns the polymer entities. \sa m_polymerEntities */ Enums::ChemicalEntity CalcOptions::getPolymerEntities() const { return m_polymerEntities; } /*! \brief Returns true if this instance is identical to \a other, false otherwise. */ bool CalcOptions::operator==(const CalcOptions &other) const { if(m_deepCalculation != other.m_deepCalculation || m_massType != other.m_massType || m_capType != other.m_capType || m_monomerEntities != other.m_monomerEntities || m_polymerEntities != other.m_polymerEntities || m_selectionType != other.m_selectionType) return false; if(mp_indexRangeCollection->size() != other.mp_indexRangeCollection->size()) return false; for(qsizetype iter = 0; iter < mp_indexRangeCollection->size(); ++iter) if(mp_indexRangeCollection->getRangeCstRefAt(iter) != other.mp_indexRangeCollection->getRangeCstRefAt(iter)) return false; return true; } /*! \brief Returns true if this instance is different than \a other, false otherwise. */ bool CalcOptions::operator!=(const CalcOptions &other) const { return !operator==(other); } /*! \brief Returns a string describing this CalcOptions instance. */ QString CalcOptions::toString() const { // qDebug() << "\n~~~~~~~~~~~~~~ CalcOptions instance ~~~~~~~~~~~~~\n" // << this << "\n" // << "m_deepCalculation:" << m_deepCalculation // << "\n m_massType:" << massTypeMap[m_massType] // << "\n m_capType:" << capTypeMap[m_capType] << "\n "; QString text; text += QString("m_deepCalculation: %1\n").arg(m_deepCalculation); text += QString("m_massType: %1\n").arg(massTypeMap[m_massType]); text += QString("m_capType: %1\n").arg(capTypeMap[m_capType]); if(!mp_indexRangeCollection->getRangesCstRef().size()) text += "No IndexRange instances\n"; else text += QString("IndexRange instances (%1 instance(s)):\n") .arg(mp_indexRangeCollection->getRangesCstRef().size()); foreach(const IndexRange *item, mp_indexRangeCollection->getRangesCstRef()) { text += QString("[%1 - %2]\n").arg(item->m_start).arg(item->m_stop); } text += QString("%1: %2\n") .arg("Monomer chemical entities") .arg(chemicalEntityMap[m_monomerEntities]); text += QString("%1: %2\n") .arg("Polymer chemical entities") .arg(chemicalEntityMap[m_polymerEntities]); text += QString("%1: %2\n") .arg("Selection type") .arg(selectionTypeMap[m_selectionType]); return text; } void CalcOptions::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register CalcOptions class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&CalcOptions::staticMetaObject); engine->globalObject().setProperty("CalcOptions", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/ChemicalGroup.cpp000664 001750 001750 00000055433 15100504560 025523 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/ChemicalGroup.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::ChemicalGroup \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile ChemicalGroup.hpp \brief The ChemicalGroup class provides a model for specifying the acido-basic behaviour of a chemical group of either a \l Monomer object or of a \l Modif object. If the ChemicalGroup does not prove sufficient to characterize precisely the acido-basic properties of an entity, \l ChemicalGroupRule instances can be added to that effect. In an pkaphpidata definition file, the following xml structure is encountered: \code A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped ..... Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE \endcode \sa ChemicalGroupRule, */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroup::m_name \brief The name of the ChemicalGroup instance. */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroup::m_pka \brief The pKa of the ChemicalGroup instance. */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroup::m_acidCharged \brief Tells if the group is charged when in acid conditions (that is, the pH is less than the pKa). */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroup::m_polymerizationRule \brief The way this ChemicalGroup behaves upon polymerization of the chemical entity into a \l Polymer. */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroup::m_rules \brief The container of \l ChemicalGroupRule instances. */ /*! \brief Constructs a ChemicalGroup instance. \a name The name of this ChemicalGroup. \a pka The pKa value of this ChemicalGroup. \a is_acid_charged Tells if the ChemicalGroup bears a charge when in acidic conditions. \a polymerization_rule Specifies the polymerization rule. Upon construction the object is validated and m_isValid is set to the result of that validation. */ ChemicalGroup::ChemicalGroup(const QString &name, float pka, bool is_acid_charged, Enums::ChemicalGroupTrapped polymerization_rule) : m_name(name), m_pka(pka), m_acidCharged(is_acid_charged), m_polymerizationRule(polymerization_rule) { if(m_pka <= 0 && m_pka >= 14) qFatalStream() << "Programming error. pKa cannot be <=0 or >= 14."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The ChemicalGroup right constructed did not validate successfully."; } /*! \brief Construct a ChemicalGroup instance as a copy of \a other. There is no checking of the validity of \a other. */ ChemicalGroup::ChemicalGroup(const ChemicalGroup &other) : m_name(other.m_name), m_pka(other.m_pka), m_acidCharged(other.m_acidCharged), m_polymerizationRule(other.m_polymerizationRule), m_isValid(other.m_isValid) { // We want a new allocation, not a shared pointer copy. for(const ChemicalGroupRuleSPtr &chemical_group_rule_sp : other.m_rules) m_rules.emplace_back( std::make_shared(*chemical_group_rule_sp.get())); } /*! \brief Destructs this ChemicalGroup instance. */ ChemicalGroup::~ChemicalGroup() { } /*! \brief Assigns \a other to this ChemicalGroup instance. \note The ChemicalGroupRule instances in \a other are deep-duplicated. Returns a reference to this ChemicalGroup instance. */ ChemicalGroup & ChemicalGroup::operator=(const ChemicalGroup &other) { if(&other == this) return *this; m_name = other.m_name; m_pka = other.m_pka; m_acidCharged = other.m_acidCharged; m_polymerizationRule = other.m_polymerizationRule; // We want a new allocation, not a shared pointer copy. for(const ChemicalGroupRuleSPtr &chemical_group_rule_sp : other.m_rules) m_rules.emplace_back( std::make_shared(*chemical_group_rule_sp.get())); ErrorList error_list; m_isValid = validate(&error_list); return *this; } /*! \brief Sets the \a name. */ void ChemicalGroup::setName(QString name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting name, the object did not validate successfully."; } } /*! \brief Returns the name. */ QString ChemicalGroup::getName() const { return m_name; } /*! \brief Sets the pKa to \a pka. */ void ChemicalGroup::setPka(float pka) { m_pka = pka; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting pKa, the object did not validate successfully."; } } /*! \brief Returns the pKa. */ float ChemicalGroup::getPka() const { return m_pka; } /*! \brief Sets the charge condition in acidic conditions to \a acid_charged. If true, the group bears a charge when the pH is less than the pKa. */ void ChemicalGroup::setAcidCharged(bool acid_charged) { m_acidCharged = acid_charged; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting acid charged bool, the object did not " "validate successfully."; } } /*! \brief Returns the charge condition in acidic conditions. If true, the group bears a charge when the pH is less than the pKa. */ bool ChemicalGroup::isAcidCharged() const { return m_acidCharged; } /*! \brief Sets the polymerization rule to \a pol_rule. The polymerization rule determines if the chemical group is trapped upon formation of a Monomer-to-Monomer bond. */ void ChemicalGroup::setPolRule(Enums::ChemicalGroupTrapped pol_rule) { m_polymerizationRule = pol_rule; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting Enums::ChemicalGroupTrapped, the object did not " "validate successfully."; } } /*! \brief Returns the polymerization rule. */ Enums::ChemicalGroupTrapped ChemicalGroup::getPolRule() const { return m_polymerizationRule; } /*! \brief Returns a const reference to the container of ChemicalGroupRule instances. */ const std::vector & ChemicalGroup::getRulesCstRef() const { return m_rules; } /*! \brief Returns a reference to the container of ChemicalGroupRule instances. */ std::vector & ChemicalGroup::getRulesRef() { return m_rules; } /*! \brief Searches by \a entity for a ChemicalGroupRule instance. Returns the \a index of the ChemicalGroupRule instance in the member list of ChemicalGroupRule instances or -1 if not found. */ ChemicalGroupRuleSPtr ChemicalGroup::findRuleByEntity(const QString &entity, std::size_t &index) const { if(index >= m_rules.size()) return nullptr; if(entity.isEmpty()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_rules.cbegin(); std::vector::const_iterator the_iterator_cst = m_rules.cbegin() + index; std::vector::const_iterator the_end_iterator_cst = m_rules.cend(); while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->getEntity() == entity) { index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Searches by \a name for a ChemicalGroupRule instance. Returns the \a index of the ChemicalGroupRule instance in the member list of ChemicalGroupRule instances or -1 if not found. */ ChemicalGroupRuleSPtr ChemicalGroup::findRuleByName(const QString &name, std::size_t &index) const { if(index >= m_rules.size()) return nullptr; if(name.isEmpty()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_rules.cbegin(); std::vector::const_iterator the_iterator_cst = m_rules.cbegin() + index; std::vector::const_iterator the_end_iterator_cst = m_rules.cend(); while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->getName() == name) { index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Searches by \a entity and \a name for a ChemicalGroupRule instance. Returns the \a index of the ChemicalGroupRule instance in the member list of ChemicalGroupRule instances or -1 if not found. */ ChemicalGroupRuleSPtr ChemicalGroup::findRuleByEntityAndName(const QString &entity, const QString &name, std::size_t &index) const { if(index >= m_rules.size()) return nullptr; if(name.isEmpty()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_rules.cbegin(); std::vector::const_iterator the_iterator_cst = m_rules.cbegin() + index; std::vector::const_iterator the_end_iterator_cst = m_rules.cend(); while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->getName() == name && (*the_iterator_cst)->getEntity() == entity) { index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Validates this instance, setting eventual error messages in \a error_list_p. \note \a error_list_p is not cleared. If the validation is successful, m_isValid is set to true, otherwise it is set to false. That result is returned. */ bool ChemicalGroup::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(m_name.isEmpty()) { qCritical() << "The ChemicalGroup cannot validate with an empty name."; error_list_p->push_back( "The ChemicalGroup cannot validate with an empty name"); } if(m_pka <= 0 && m_pka >= 14) { qCritical() << "The ChemicalGroup cannot validate with an invalid pKa value."; error_list_p->push_back( "The ChemicalGroup cannot validate with an invalid pKa value."); } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this instance. */ bool ChemicalGroup::isValid() const { return m_isValid; } /*! \brief Parses the ChemicalGroup XML \a element \e{related to a \l Monomer}. Upon parsing of the \a element (tag \code{}), its data are validated and set to this ChemicalGroup instance, thus essentially initializing it. In an pkaphpidata definition file, the following xml structure is encountered: \code A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped \endcode Upon parsing of the \a element, all the data are validated and set to this ChemicalGroup instance, thus essentially initializing it. If there are \l{ChemicalGroupRule}s associated to the ChemicalGroup element, these are rendered also. Returns true if parsing and validation were successful, false otherwise. */ bool ChemicalGroup::renderXmlMnmElement(const QDomElement &element) { // The element the parameter points to is: // // // // Which means that element.tagName() == "mnmchemgroup" and that we'll // have to go one step down to the first child of the current node // in order to get to the \code\endcode element. QDomElement child; if(element.tagName() != "mnmchemgroup") return false; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "pka") return false; bool ok = false; m_pka = child.text().toFloat(&ok); if(!m_pka && !ok) return false; if(m_pka <= 0 || m_pka >= 14) return false; child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "acidcharged") return false; if(child.text() != "FALSE" && child.text() != "TRUE") return false; m_acidCharged = (child.text() == "FALSE" ? false : true); // And now the polrule element. There should be one, here, in fact, // because we are dealing with a monomer, and not a modification. child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "polrule") return false; if(child.text() == "never_trapped") m_polymerizationRule = Enums::ChemicalGroupTrapped::NEVER; else if(child.text() == "left_trapped") m_polymerizationRule = Enums::ChemicalGroupTrapped::LEFT; else if(child.text() == "right_trapped") m_polymerizationRule = Enums::ChemicalGroupTrapped::RIGHT; else return false; // And finally the chemical group rules... There might be zero, one // or more. QDomElement childChemGroupRule = child.nextSiblingElement("chemgrouprule"); while(!childChemGroupRule.isNull()) { ChemicalGroupRuleSPtr chemical_group_rule_sp = std::make_shared(); if(!chemical_group_rule_sp->renderXmlElement(childChemGroupRule)) { chemical_group_rule_sp.reset(); return false; } m_rules.push_back(chemical_group_rule_sp); childChemGroupRule = childChemGroupRule.nextSiblingElement(); } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate successfully, with errors:" << Utils::joinErrorList(error_list); return m_isValid; } /*! \brief Parses the ChemicalGroup XML \a element \e{related to a \l Modif}. Upon parsing of the \a element (tag ), its data are validated and set to this ChemicalGroup instance, thus essentially initializing it. In an pkaphpidata definition file, the following xml structure is encountered: \code A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped ....... Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE \endcode Upon parsing of the \a element, all the data are validated and set to this ChemicalGroup instance, thus essentially initializing it. If there are \l{ChemicalGroupRule}s associated to the ChemicalGroup element, these are rendered also. Returns true if parsing and validation were successful, false otherwise. */ bool ChemicalGroup::renderXmlMdfElement(const QDomElement &element) { QDomElement child; if(element.tagName() != "mdfchemgroup") return false; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "pka") return false; bool ok = false; m_pka = child.text().toFloat(&ok); if(!m_pka && !ok) return false; if(m_pka <= 0 || m_pka >= 14) return false; child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "acidcharged") return false; if(child.text() != "FALSE" && child.text() != "TRUE") return false; m_acidCharged = (child.text() == "FALSE" ? false : true); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate successfully, with errors:" << Utils::joinErrorList(error_list); return m_isValid; } //////////////////////// ChemicalGroupProp //////////////////////// //////////////////////// ChemicalGroupProp //////////////////////// /*! \class MsXpS::libXpertMassCore::ChemicalGroupProp \inmodule libXpertMassCore \ingroup ThePropSystem \brief The ChemicalGroupProp class provides a Prop instance of which the member data points to a dynamically allocated \l ChemicalGroup instance. */ /*! \brief Constructs a ChemicalGroupProp instance using \a data and \a name. The \a data pointer is set to the \l mpa_data member. */ ChemicalGroupProp::ChemicalGroupProp(const QString &name, ChemicalGroup *data) { if(!name.isEmpty()) m_name = name; else m_name = QString(); mpa_data = static_cast(data); } /*! \brief Constructs a ChemicalGroupProp instance as a copy of \a other. The data in \a other are duplicated and set to this ChemicalGroupProp instance. */ ChemicalGroupProp::ChemicalGroupProp(const ChemicalGroupProp &other) : Prop(other) { if(other.mpa_data != nullptr) { ChemicalGroup *chemicalGroup = static_cast(other.mpa_data); mpa_data = static_cast(new ChemicalGroup(*chemicalGroup)); } else mpa_data = nullptr; } /*! \brief Destructs this ChemicalGroupProp instance. The deletion of the data are delegated to \l deleteData(). */ ChemicalGroupProp::~ChemicalGroupProp() { deleteData(); } /*! \brief Deletes the member data. */ void ChemicalGroupProp::deleteData() { if(mpa_data != nullptr) { delete static_cast(mpa_data); mpa_data = nullptr; } } /*! \brief Assigns \a other to this ChemicalGroupProp instance. The member data are first deleted and then set to a copy of those in \a other. */ ChemicalGroupProp & ChemicalGroupProp::operator=(const ChemicalGroupProp &other) { if(&other == this) return *this; Prop::operator=(other); if(mpa_data != nullptr) deleteData(); if(other.mpa_data != nullptr) { ChemicalGroup *chemicalGroup = static_cast(other.mpa_data); mpa_data = static_cast(new ChemicalGroup(*chemicalGroup)); } else mpa_data = nullptr; return *this; } /*! \brief Duplicates this ChemicalGroupProp instance and returns its pointer. */ ChemicalGroupProp * ChemicalGroupProp::cloneOut() const { ChemicalGroupProp *new_p = new ChemicalGroupProp(*this); return new_p; } bool ChemicalGroupProp::renderXmlElement([[maybe_unused]] const QDomElement &element, [[maybe_unused]] int version) { return false; } QString ChemicalGroupProp::formatXmlElement([[maybe_unused]] int offset, [[maybe_unused]] const QString &indent) { return QString(); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/ChemicalGroupRule.cpp000664 001750 001750 00000023165 15100504560 026350 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/ChemicalGroupRule.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::ChemicalGroupRule \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile ChemicalGroupRule.hpp \brief The ChemicalGroupRule class provides a model for refining the acido-basic behaviour of a chemical group of either a \l Monomer object or of a \l Modif object. In an pkaphpidata definition file, the following xml structure is encountered: \code A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped ..... Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE \endcode \sa ChemicalGroup, */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroupRule::m_name \brief The name of the ChemicalGroupRule instance. */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroupRule::m_entity \brief The entity of the ChemicalGroupRule instance, like LE_PLM_MODIF for \e{left end polymer modification}. */ /*! \variable MsXpS::libXpertMassCore::ChemicalGroupRule::m_chemicalGroupFate \brief The fate of the ChemicalGroupRule instance. \sa MsXpS::libXpertMassCore::Enums::ChemicalGroupFate */ /*! \brief Constructs a ChemicalGroupRule instance. \list \li \a name: The name of this ChemicalGroupRule instance. \li \a entity: The entity of this ChemicalGroupRule instance. \li \a fate: The fate of this ChemicalGroupRule instance. \endlist */ ChemicalGroupRule::ChemicalGroupRule(const QString &name, const QString &entity, Enums::ChemicalGroupFate fate) : m_name(name), m_entity(entity), m_chemicalGroupFate(fate) { Q_ASSERT(m_chemicalGroupFate == Enums::ChemicalGroupFate::LOST || m_chemicalGroupFate == Enums::ChemicalGroupFate::PRESERVED); } /*! \brief Constructs a ChemicalGroupRule instance as a copy of \a other. */ ChemicalGroupRule::ChemicalGroupRule(const ChemicalGroupRule &other) : m_name(other.m_name), m_entity(other.m_entity), m_chemicalGroupFate(other.m_chemicalGroupFate), m_isValid(other.m_isValid) { } /*! \ brief Constructs this ChemicalGroupRule */ ChemicalGroupRule::~ChemicalGroupRule() { } /*! \brief Sets the \a name. */ void ChemicalGroupRule::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting name, the object did not validate successfully."; } } /*! \brief Returns the name. */ QString ChemicalGroupRule::getName() { return m_name; } /*! \brief Sets the \a entity. */ void ChemicalGroupRule::setEntity(const QString &entity) { m_entity = entity; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting entity, the object did not validate successfully."; } } /*! \brief Returns the entity. */ QString ChemicalGroupRule::getEntity() { return m_entity; } /*! \brief Sets the \a fate. */ void ChemicalGroupRule::setFate(Enums::ChemicalGroupFate fate) { m_chemicalGroupFate = fate; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "After setting fate, the object did not validate successfully."; } } /*! \brief Returns the fate. */ Enums::ChemicalGroupFate ChemicalGroupRule::getFate() { return m_chemicalGroupFate; } /*! \brief Assigns \a other to this instance. */ ChemicalGroupRule & ChemicalGroupRule::operator=(const ChemicalGroupRule &other) { if(&other == this) return *this; m_name = other.m_name; m_entity = other.m_entity; m_chemicalGroupFate = other.m_chemicalGroupFate; m_isValid = other.m_isValid; return *this; } /*! \brief Validates this instance, setting eventual error messages to \a error_list_p. \note \a error_list_p is not cleared. Following a successful validation, m_isValid is set to true, to false otherwise and that result is returned. */ bool ChemicalGroupRule::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(m_name.isEmpty()) { qCritical() << "The ChemicalGroupRule cannot validate with an empty name."; error_list_p->push_back( "The ChemicalGroupRule cannot validate with an empty name"); } if(m_entity.isEmpty()) { qCritical() << "The ChemicalGroupRule cannot validate with an empty entity."; error_list_p->push_back( "The ChemicalGroupRule cannot validate with an empty entity"); } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this instance. */ bool ChemicalGroupRule::isValid() const { return m_isValid; } /*! \brief Parses the ChemicalGroupRule XML \a element. Upon parsing of the \a element, its data are validated and set to this ChemicalGroupRule instance, thus essentially initializing it. Returns true if parsing and validation were successful, false otherwise. */ bool ChemicalGroupRule::renderXmlElement(const QDomElement &element) { QDomElement child; // In an acidobasic definition file, the following xml structure // is encountered: // // C // // N-term NH2 // 9.6 // TRUE // left_trapped // // LE_PLM_MODIF // Acetylation // LOST // // // The relevant DTD line is: // // And the element the parameter points to is: // // Which means that element.tagName() == "chemgrouprule" and that we'll // have to go one step down to the first child of the current node // in order to get to the element. if(element.tagName() != "chemgrouprule") { qCritical() << "Failed to render element: " " element not found."; return false; } child = element.firstChildElement("entity"); if(child.isNull()) { qCritical() << "Failed to render element: " "element not found."; return false; } m_entity = child.text(); child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "name") { qCritical() << "Failed to render element: element " "not found."; return false; } m_name = child.text(); child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "outcome") { qCritical() << "Failed to render element: " "element not found."; return false; } if(child.text() == "LOST") m_chemicalGroupFate = Enums::ChemicalGroupFate::LOST; else if(child.text() == "PRESERVED") m_chemicalGroupFate = Enums::ChemicalGroupFate::PRESERVED; else { qCritical() << "Failed to render element: " "element had bad value."; return false; } ErrorList error_list; m_isValid = validate(&error_list); return m_isValid; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CleavageAgent.cpp000664 001750 001750 00000102665 15100504560 025467 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CleavageAgent.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CleavageAgent \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CleavageAgent.hpp \brief The CleavageAgent class provides a model for specifying aqueous cleavage specifications (patterns) of \l{Polymer} \l{Sequence}s. Cleavage specifications determine the specificity of cleavage in a polymer sequence using a simple syntax. For example, Trypsin is able to cleave after lysyl and arginyl residues. Its cleavage pattern is thus "Lys/;Arg/". However, it is known that Trypsin fails to cleave after Lys if that monomer is followed by a Prolyl residue, thus the complete cleavage agent specification for Trypsin comprises three cleavage patterns: "Lys/;Arg/;-Lys/Pro". A cleavage agent specification might not be enough information to determine the manner in which a polymer is cleaved. Cleavage rules might be required to refine the specification. A cleavage agent specification might hold as many cleavage rules as required. \sa CleavageMotif, CleavageRule */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::m_name \brief The name of the CleavageAgent. */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::m_pattern \brief The cleavage pattern, that might comprise more than one \l CleavageMotif. \sa CleavageMotif, CleavageRule */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::m_motifs \brief The container of \l{CleavageMotif}s that together make the CleavageAgent. \sa CleavageMotif */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::m_rules \brief The container of \l{CleavageRule}s that might be requied to refine the CleavageAgent. \sa CleavageRule */ /*! \variable MsXpS::libXpertMassCore::CleavageAgent::m_isValid \brief The validity status of the instance. */ /*! \typedef MsXpS::libXpertMassCore::CleavageAgentSPtr \relates MsXpS::libXpertMassCore::CleavageAgent. Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::CleavageAgentCstSPtr \relates MsXpS::libXpertMassCore::CleavageAgent. Synonym for std::shared_ptr. */ /*! \brief Constructs a totally empty CleavageAgent instance */ CleavageAgent::CleavageAgent(QObject *parent): QObject(parent) { } /*! \brief Constructs a CleavageAgent instance starting from an XML \a element according to \a version and a \a pol_chem_def_csp PolChemDef. The \a version indicates what version of the XML element is to be used. Before version 2, the CleavageAgent class was named CleaveSpec and thus the XML element was called : This is the current format (FAKE cls): \code CyanogenBromide M/A M -C1H2S1+O1 A -CH3COOH \endcode After version 2, the CleaveSpec class has been named CleavageAgent and thus the XML element is now called .The inner structure of the element has not changed. Depending on the \a version argument, one function (older) or the other (current) is used to parse the XML element. The rendering of the CleavageRule instances requires that the PolChemDef be available. \sa renderXmlClsElement(), renderXmlClaElement() */ CleavageAgent::CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version, QObject *parent) : QObject(parent), mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing CleavageAgent with no PolChemDef."; if(version == 1) { qDebug() << "Rendering XmlClsElement version" << version; if(!renderXmlClsElement(element, version)) qCritical() << "Failed to fully render or validate the CleavageAgent XML element " "for construction of CleavageAgent instance."; } else if(version == 2) { qDebug() << "Rendering XmlClaElement version" << version; if(!renderXmlClaElement(element, version)) qCritical() << "Failed to fully render or validate the CleavageAgent XML element " "for construction of CleavageAgent instance."; } else qFatalStream() << "Programming error. The polymer chemistry definition version " "is not correct."; } /*! \brief Constructs a CleavageAgent instance. \list \li \a pol_chem_def_csp: The polymer chemistry definition. \li \a name: The name of the CleavageAgent. \li \a pattern: The pattern of the CleavageAgent, like ("Lys/;Arg/;-Lys/Pro"). \endlist Upon setting all the member data, the \a pattern is parsed. After this, this instance is validated and the member m_isValid is set to the result. */ CleavageAgent::CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &pattern, QObject *parent) : QObject(parent), mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_pattern(pattern) { if(!parse()) qCritical() << "Upon construction of the CleavageAgent, the pattern could not " "be parsed."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of the CleavageAgent, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a CleavageAgent instance as a copy of \a other. The copying of the member containers of CleavageMotif and CleavageRule instances is shallow (only the shared pointers are copied). Upon setting all the member data, this instance is validated and the member m_isValid is set to the result. */ CleavageAgent::CleavageAgent(const CleavageAgent &other, QObject *parent) : QObject(parent), mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_pattern(other.m_pattern), m_motifs(other.m_motifs), m_rules(other.m_rules) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon copy-construction of the CleavageAgent, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destructs this CleavageAgent instance. */ CleavageAgent::~CleavageAgent() { } /*! \brief Initialized this CleavageAgent using \a other. After initialization, validity is checked, the result is set to m_isValid and is returned. */ bool CleavageAgent::initialize(const CleavageAgent &other) { mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_pattern = other.m_pattern; m_motifs.assign(other.m_motifs.begin(), other.m_motifs.end()); m_rules.assign(other.m_rules.begin(), other.m_rules.end()); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to initalize the CleavageAgent instance."; return m_isValid; } //////////////// THE POLCHEMDEF ///////////////////// /*! \brief Sets the PolChemDef to \a pol_chem_def_csp. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void CleavageAgent::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting PolChemDef of CleavageAgent, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the PolChemDef. */ PolChemDefCstSPtr CleavageAgent::getPolchemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the name to \a name. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void CleavageAgent::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting name of CleavageAgent, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ const QString & CleavageAgent::getName() const { return m_name; } //////////////// THE PATTERN ///////////////////// /*! \brief Sets the \a pattern. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void CleavageAgent::setPattern(const QString &pattern) { m_pattern = pattern; if(!parse()) qCritical() << "The pattern that was set could not be parsed."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting pattern of CleavageAgent, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the pattern. */ const QString & CleavageAgent::getPattern() const { return m_pattern; } /*! \brief Returns a const reference to the container of CleavageMotif instances. */ const std::vector & CleavageAgent::getMotifsCstRef() const { return m_motifs; } /*! \brief Returns a reference to the container of CleavageMotif instances. */ std::vector & CleavageAgent::getMotifsRef() { return m_motifs; } /*! \brief Returns a const reference to the container of CleavageRule instances. */ const std::vector & CleavageAgent::getRulesCstRef() const { return m_rules; } /*! \brief Returns a reference to the container of CleavageRule instances. */ std::vector & CleavageAgent::getRulesRef() { return m_rules; } /*! \brief Returns the CleavageRule that has name \a name, or nullptr if not found. */ CleavageRuleSPtr CleavageAgent::getCleavageRuleSPtrByName(const QString &name) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_rules.cbegin(), m_rules.cend(), [name](const CleavageRuleSPtr &cleavage_rule_sp) { return cleavage_rule_sp->getName() == name; }); if(the_iterator_cst == m_rules.cend()) return nullptr; return *the_iterator_cst; } //////////////// PARSING ///////////////////// /*! \brief Parses this CleavageAgent instance. The parsing involves separating the components found in the m_pattern string and making CleavageMotif instances out of them. Starting from a pattern "Lys/;Arg/;-Lys/Pro", the parsing would first split it into three cleavage site strings: \list \li "Lys/"; \li "Arg/"; \li "-Lys/Pro" \endlist Each of these site strings will be deconstructed into motifs, stored in CleavageMotif objects: \list \li First motif has a code container "[0] Lys", an offset of 1 and is for cleavage; \li Second motif has a code container "[0] Arg", an offset of 1 and is for cleavage; \li Third motif has a code container "[0] Lys, [1] Pro", an offset of 1 and is not for cleavage (mind the '-') \endlist If the pattern string is empty, m_isValid is set to false and false is returned. Returns true if parsing is successful, false otherwise. */ bool CleavageAgent::parse() { if(m_pattern.isEmpty()) { m_isValid = false; return false; } // The 'm_pattern' is a ';'-delimited string, in which each // sub-string is a 'site'. Each site is in turn constituted by a // motif and a '/' that indicates where the motif is actually // cleaved. // // For example the "-Lys/Pro" site is actually a motif of sequence // "LysPro" and the site holds two more informations with respect to // the mere motif: it says that the motif should not be cleaved //('-') and that if the '-' were not there, the cleavage would // occur between the Lys and the Pro('/' symbolizes the cleavage). // For example, if the cleavageagent had a "Lys/;Arg/;-Lys/Pro" string, // it would be split into 3 strings: "Lys/" and "Arg/" and // "-Lys/Pro"(these are 'site' strings). These three site string // would further be deconstructed into motif string(removal of '-' // and '/' characters). Where would these 3 motif strings be stored? // They would be set into one cleavagemotif instance for each // motif. Thus, for example, "-Lys/Pro" would yield a cleavagemotif of // 'motif' LysPro, with a FALSE cleave member and a 1 offset member. // Will return the number of cleavagemotif instances that were created. // Upon error -1 is returned. // "Lys/;Arg/;-Lys/Pro" --> [0] Lys/, [1] Arg/, [2] -Lys/Pro QStringList sites = m_pattern.split(";", Qt::SkipEmptyParts); // for (int iter = 0; iter < sites.size() ; ++iter) // qDebug() << __FILE__ << __LINE__ // << sites.at(iter); Enums::CleavageAction cleavage_action; for(int iter = 0; iter < sites.size(); ++iter) { cleavage_action = Enums::CleavageAction::CLEAVE; QString iter_cleavage_site = sites.at(iter); if(iter_cleavage_site.length() < 2) { qCritical() << iter_cleavage_site << "is an invalid cleavage site."; m_isValid = false; return false; } int count = iter_cleavage_site.count(QChar('/')); if(count != 1) { qCritical() << "The must be one and only one '/' in the cleavage site."; m_isValid = false; return false; } // Remove spaces. iter_cleavage_site.remove(QRegularExpression("\\s+")); // Is there a '-' in the site string indicating that this site // is NOT for cleavage? If so, there should be only one such // sign and in position 0. count = iter_cleavage_site.count(QChar('-')); if(count > 1) { qCritical() << "There must be at most one '-' in the cleavage site."; m_isValid = false; return false; } else if(count == 1) { if(iter_cleavage_site.indexOf(QChar('-'), 0, Qt::CaseInsensitive) != 0) { qCritical() << "The '-' in the cleavage site must be at the " "beginning of the cleavage site."; m_isValid = false; return false; } cleavage_action = Enums::CleavageAction::NO_CLEAVE; // We can remove the '-' character, as we now know // that the site is NOT for cleavage. iter_cleavage_site.remove(0, 1); } // else: site is for cleavage: no '-' found. // We can create a new cleavage motif. CleavageMotifSPtr cleavage_motif_sp = std::make_shared(mcsp_polChemDef); cleavage_motif_sp->setCleavageAction(cleavage_action); cleavage_motif_sp->parseSite(iter_cleavage_site); if(!cleavage_motif_sp->isValid()) { qCritical() << "Failed to create a valid CleavageMotif by parsing " "cleavage site:" << iter_cleavage_site; cleavage_motif_sp.reset(); return false; } m_motifs.push_back(cleavage_motif_sp); } return true; } //////////////// OPERATORS ///////////////////// /*! \brief Returns true if \c this and \a other are identical. Because the CleavageMotifSPtr in m_motifs and the CleavageRuleSPtr in m_rules do not necessarily reference data from the PolChemDef, the comparison of the two instances involves a deep comparison (not a comparison of the pointers themselves). */ bool CleavageAgent::operator==(const CleavageAgent &other) const { if(&other == this) return true; // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). if(m_name != other.m_name || m_pattern != other.m_pattern) { qDebug() << "Either the name or the pattern or both is/are different."; return false; } if(m_motifs.size() != other.m_motifs.size()) { qDebug() << "The CleavageMotif containers have differing sizes."; return false; } for(std::size_t iter = 0; iter < m_motifs.size(); ++iter) { // qDebug() << "Will compare two motifs:" << m_motifs.at(iter)->getMotif() // << "versus" << other.m_motifs.at(iter)->getMotif(); if(*m_motifs.at(iter).get() != *other.m_motifs.at(iter).get()) { qDebug() << "At least one CleavageMotif is different in both CleavageAgent " "instances."; return false; } } if(m_rules.size() != other.m_rules.size()) { qDebug() << "The CleavageRule containers have differing sizes."; return false; } for(std::size_t iter = 0; iter < m_rules.size(); ++iter) { if(*m_rules.at(iter).get() != *other.m_rules.at(iter).get()) { qDebug() << "At least one CleavageMotif is different in both CleavageAgent " "instances."; return false; } } return true; } /*! \brief Returns true if \c this and \a other are different. */ bool CleavageAgent::operator!=(const CleavageAgent &other) const { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns the CleavageAgent instance from the polymer chemistry definition registered in this instance. The key to search the CleavageAgent is this instance's member name. If there is no PolChemDef available, nullptr is returned. If no CleavageAgent instance is found by this instance's name, nullptr is returned. */ CleavageAgentCstSPtr CleavageAgent::getFromPolChemDefByName() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_name.isEmpty()) return nullptr; return mcsp_polChemDef->getCleavageAgentCstSPtrByName(m_name); } /*! \brief Returns the status of this CleavageAgent instance the polymer chemistry definition registered in this instance. The key to search the CleavageAgent is this instance's member name. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no CleavageAgent instance is found by this instance's name, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus CleavageAgent::isKnownByNameInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getCleavageAgentCstSPtrByName(m_name) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; } /*! \brief Returns the CleavageRule from the container that has \a name. If no \l CleavageRule is found, nullptr is returned. */ CleavageRuleCstSPtr CleavageAgent::getCleavageRuleCstSPtrByName(const QString &name) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_rules.cbegin(), m_rules.cend(), [name](const CleavageRuleSPtr &cleave_rule_sp) { return cleave_rule_sp->getName() == name; }); if(the_iterator_cst == m_rules.cend()) return nullptr; return *(the_iterator_cst); } /*! \brief Returns the index of the CleavageRule instance named \a name in the member container of CleavageRule intances. If no CleavageRule by name \a name is found, -1 is returned. */ int CleavageAgent::getCleavageRuleIndexByName(const QString &name) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_rules.cbegin(), m_rules.cend(), [name](const CleavageRuleSPtr &cleave_rule_sp) { return cleave_rule_sp->getName() == name; }); if(the_iterator_cst == m_rules.cend()) return -1; return std::distance(m_rules.cbegin(), the_iterator_cst); } /*! \brief Validates this CleavageAgent instance setting error messages to \a error_list_p. The validation involves checking that: \list \li The PolChemDef must be available; \li The name is not empty; \li The pattern is nt empty; \li The parsing of the pattern is successful.; \li Each CleavageRule instance (if any) validates successfully. \endlist Returns true if the validation is successful, false otherwise. */ bool CleavageAgent::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "A CleavageAgent with no PolChemDef available cannot " "validate successfully."; error_list_p->push_back( "A CleavageAgent with no PolChemDef available cannot validate " "successfully"); } if(m_name.isEmpty()) { qCritical() << "A CleavageAgent with no name cannot validate successfully."; error_list_p->push_back( "A CleavageAgent with no name cannot validate successfully"); } if(m_pattern.isEmpty()) { qCritical() << "A CleavageAgent with no pattern cannot validate successfully."; error_list_p->push_back( "A CleavageAgent with no pattern cannot validate successfully"); } // If there are motifs, we have to check them all. for(const CleavageMotifSPtr &cleavage_motif_sp : m_motifs) { if(!cleavage_motif_sp->validate(error_list_p)) { qCritical() << "A CleavageAgent with invalid CleavageMotif instances " "cannot validate successfully."; error_list_p->push_back( "A CleavageAgent with invalid CleavageMotif instances cannot " "validate " "successfully"); break; } } // If there are rules, we have to check them all. for(const CleavageRuleSPtr &cleavage_rule_sp : m_rules) { if(!cleavage_rule_sp->validate(error_list_p)) { qCritical() << "A CleavageAgent with invalid CleavageRule instances " "cannot validate successfully."; error_list_p->push_back( "A CleavageAgent with invalid CleavageRule instances cannot " "validate " "successfully"); break; } } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this instance. */ bool CleavageAgent::isValid() const { return m_isValid; } //////////////// XML DATA LOADING WRITING ///////////////////// /*! \brief Parses a cleavage specification XML \a element using a \a{version}ed function. The data in the element are validated and if successful they are set to this instance, thus initializing it. After setting the member data, the instance is validated and the result is set to m_isValid that is returned. */ bool CleavageAgent::renderXmlClsElement(const QDomElement &element, [[maybe_unused]] int version) { // For the time being the version is not necessary here. As of // version up to 2, the current function works ok. QDomElement child; QDomElement childRule; /* The xml node we are in is structured this way: * * * CyanogenBromide * M/ * * M * -C1H2S1+O1 * M * -C1H2S1+O1 * * * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "cls" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. */ if(element.tagName() != "cls") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The CleaveSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); // qDebug() << "The name:" << m_name; child = child.nextSiblingElement("pattern"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The CleaveSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_pattern = child.text(); if(!parse()) { qCritical() << "The CleaveSpec being initialized with a XML " "element had an unparseable pattern."; m_isValid = false; return false; } // At this point there might be 0, 1 or more cleavage rules. child = child.nextSiblingElement("clr"); while(!child.isNull()) { CleavageRuleSPtr cleavage_rule_sp = std::make_shared(mcsp_polChemDef); if(!cleavage_rule_sp->renderXmlClrElement(child, version)) { qCritical() << "Failed to render CleaveRule from XML element."; m_isValid = false; cleavage_rule_sp.reset(); return false; } ErrorList error_list; if(!cleavage_rule_sp->validate(&error_list)) { qCritical() << "Failed to validate CleaveRule rendered from " "XML element, with errors:"; Utils::joinErrorList(error_list, ", "); cleavage_rule_sp.reset(); return false; } m_rules.push_back(cleavage_rule_sp); child = child.nextSiblingElement("clr"); } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate CleaveSpec instance right after " "rendering it from XML element, with errors:" << Utils::joinErrorList(error_list, ", "); return m_isValid; } /*! \brief Parses a cleavage agent XML \a element using a \a{version}ed function. The data in the element are validated and if successful they are set to this instance, thus initializing it. After setting the member data, the instance is validated and the result is set to m_isValid that is returned. */ bool CleavageAgent::renderXmlClaElement(const QDomElement &element, [[maybe_unused]] int version) { // For the time being the version is not necessary here. As of // version up to 2, the current function works ok. QDomElement child; QDomElement childRule; /* The xml node we are in is structured this way: * * * CyanogenBromide * M/ * * M * -C1H2S1+O1 * M * -C1H2S1+O1 * * * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "cla" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. */ if(element.tagName() != "cla") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The CleavageAgent did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); qDebug() << "The CleavageAgent has name:" << m_name; child = child.nextSiblingElement("pattern"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The CleavageAgent did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_pattern = child.text(); qDebug() << "The CleavageAgent has pattern:" << m_pattern; if(!parse()) { qCritical() << "The CleavageAgent being initialized with a XML " "element had an unparseable pattern."; m_isValid = false; return false; } qDebug() << "The CleavageAgent pattern was parsed successfully."; // At this point there might be 0, 1 or more cleavage rules. child = child.nextSiblingElement("clr"); while(!child.isNull()) { qDebug() << "Iterating in child element."; CleavageRuleSPtr cleavage_rule_sp = std::make_shared(mcsp_polChemDef); qDebug() << "Will now render the element."; if(!cleavage_rule_sp->renderXmlClrElement(child, version)) { qCritical() << "Failed to render CleavageRule from XML element."; m_isValid = false; cleavage_rule_sp.reset(); return false; } qDebug() << "The element was parsed successfully."; ErrorList error_list; if(!cleavage_rule_sp->validate(&error_list)) { qCritical() << "Failed to validate CleavageRule rendered from " "XML element, with errors:"; Utils::joinErrorList(error_list, ", "); cleavage_rule_sp.reset(); return false; } qDebug() << "Now storing the CleavageRule in the container."; m_rules.push_back(cleavage_rule_sp); child = child.nextSiblingElement("clr"); } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate CleavageAgent instance right after " "rendering it from XML element, with errors:" << Utils::joinErrorList(error_list, ", "); else qDebug() << "Successfully parsed the element."; return m_isValid; } /*! \brief Formats and returns a string describing this CleavageAgent instance in a format suitable to be used as an XML element. The XML element is typically used in a polymer chemistry defintion and looks like this: \code CyanogenBromide M/ M -CH2S+O \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a string. */ QString CleavageAgent::formatXmlClaElement(int offset, const QString &indent) const { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n").arg(lead).arg(m_pattern); for(const CleavageRuleSPtr &cleavage_rule_sp : m_rules) text += cleavage_rule_sp->formatXmlClrElement(newOffset); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } //////////////// UTILS ///////////////////// /*! \brief Returns a string describing this CleavageAgent instance. */ QString CleavageAgent::toString() const { return QString("%1 - %2 - %3 motifs - %4 rules") .arg(m_name) .arg(m_pattern) .arg(m_motifs.size()) .arg(m_rules.size()); } void CleavageAgent::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register CleavageAgent class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&CleavageAgent::staticMetaObject); engine->globalObject().setProperty("CleavageAgent", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CleavageConfig.cpp000664 001750 001750 00000022062 15100504560 025626 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CleavageConfig.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CleavageConfig \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CleavageConfig.hpp \brief The CleavageConfig class derives from CleavageAgent to provide a model for specifying aqueous cleavage specifications (patterns) of \l{Polymer} \l{Sequence}s along with instructions on the way the cleavage must occur. Cleavage agent specifications determine the specificity of cleavage in a polymer sequence using a simple syntax. For example, Trypsin is able to cleave after lysyl and arginyl residues. Its cleavage pattern is thus "Lys/;Arg/". However, it is known that Trypsin fails to cleave after Lys if that monomer is followed by a Prolyl residue, thus the complete cleavage agent pattern specification for Trypsin is the following: "Lys/;Arg/;-Lys/Pro". A cleavage agent specification might not be enough information to determine the manner in which a polymer is cleaved. Cleavage rules might be required to refine the specification. A cleavage agent specification might hold as many cleavage rules as required. \sa CleavageMotif, CleavageRule */ /*! \variable MsXpS::libXpertMassCore::CleavageConfig::m_partials \brief The partial cleavages accepted. */ /*! \variable MsXpS::libXpertMassCore::CleavageConfig::m_startIonizeLevel \brief The first ionization level of the range. */ /*! \variable MsXpS::libXpertMassCore::CleavageConfig::m_stopIonizeLevel \brief The last ionization level of the range. */ /*! \variable MsXpS::libXpertMassCore::CleavageConfig::m_sequenceEmbedded \brief Tells if the sequence of the obtained Oligomer instance need to embed their sequence. */ /*! \brief Constructs an empty CleavageConfig instance. */ CleavageConfig::CleavageConfig(QObject *parent): CleavageAgent(parent) { } /*! \brief Constructs a CleavageConfig instance using a full set of parameters. \list \a pol_chem_def_csp: the polymer chemistry definition. \a name: the name of CleavageAgent; \a pattern: the cleavage pattern of the CleavageAgent; \a partials: the partial cleavages; \a is_sequence_embedded: indicates if the Oligomer sequence needs to be stored. \endlist */ CleavageConfig::CleavageConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &pattern, int partials, bool is_sequence_embedded, QObject *parent) : CleavageAgent(pol_chem_def_csp, name, pattern, parent), m_partials(partials), m_sequenceEmbedded(is_sequence_embedded) { } /*! \brief Constructs a CleavageConfig instance starting using a limited set of parameters. \list \a cleavage_agent: CleavageAgent to be used to initialize the base class. \a partials: the partial cleavages; \a is_sequence_embedded: indicates if the Oligomer sequence needs to be stored. \endlist */ CleavageConfig::CleavageConfig(const CleavageAgent &cleavage_agent, int partials, bool is_sequence_embedded, QObject *parent) : CleavageAgent(cleavage_agent, parent), m_partials(partials), m_sequenceEmbedded(is_sequence_embedded) { } /*! \brief Constructs a CleavageConfig instance as a copy of \a other */ CleavageConfig::CleavageConfig(const CleavageConfig &other, QObject *parent) : CleavageAgent(other, parent), m_partials(other.m_partials), m_startIonizeLevel(other.m_startIonizeLevel), m_stopIonizeLevel(other.m_stopIonizeLevel), m_sequenceEmbedded(other.m_sequenceEmbedded) { } /*! \brief Destructs this CleavageConfig instance. */ CleavageConfig::~CleavageConfig() { } /*! \brief Initializes this CleavageConfig instance using \a other. If no initialization error occurred, return true, false otherwise. */ bool CleavageConfig::initialize(const CleavageConfig &other) { // That call fatal-fails, no need to check bool return value. bool ok = CleavageAgent::initialize(other); if(!ok) qCritical() << "Failed to initalize the CleavageConfig instance."; m_partials = other.m_partials; m_startIonizeLevel = other.m_startIonizeLevel; m_stopIonizeLevel = other.m_stopIonizeLevel; m_sequenceEmbedded = other.m_sequenceEmbedded; return ok; } /*! \brief Sets this instance's CleavageAgent base class to \a cleavage_agent. Returns true the CleavageAgent initialization succeeded, false otherwise. */ bool CleavageConfig::setCleavageAgent(const CleavageAgent &cleavage_agent) { bool ok = CleavageAgent::initialize(cleavage_agent); if(!ok) qCritical() << "Failed to initalize the CleavageConfig instance."; return ok; } /*! \brief Set the partial cleavages to \a value. */ void CleavageConfig::setPartials(int value) { Q_ASSERT(value >= 0); m_partials = value; } /*! \brief Returns the partial cleavages. */ int CleavageConfig::getPartials() const { return m_partials; } /*! \brief Sets the ionization start level to \a value. */ void CleavageConfig::setStartIonizeLevel(int value) { int local = (value < 0) ? abs(value) : value; if(local <= m_stopIonizeLevel) { m_startIonizeLevel = local; } else { m_startIonizeLevel = m_stopIonizeLevel; m_stopIonizeLevel = local; } } /*! \brief Returns the ionization start level. */ int CleavageConfig::getStartIonizeLevel() const { return m_startIonizeLevel; } /*! \brief Sets the ionization stop level to \a value. */ void CleavageConfig::setStopIonizeLevel(int value) { int local = (value < 0) ? abs(value) : value; if(local > m_startIonizeLevel) { m_stopIonizeLevel = local; } else { m_startIonizeLevel = m_stopIonizeLevel; m_stopIonizeLevel = local; } } /*! \brief Returns the ionization stop level. */ int CleavageConfig::getStopIonizeLevel() const { return m_stopIonizeLevel; } /*! \brief Sets both the ionization start and stop levels to \a value1 and \a value2, respectively. */ void CleavageConfig::setIonizeLevels(int value1, int value2) { if(abs(value1) <= abs(value2)) { m_startIonizeLevel = abs(value1); m_stopIonizeLevel = abs(value2); } else { m_startIonizeLevel = abs(value2); m_stopIonizeLevel = abs(value1); } } /*! \brief Sets the sequence embedded boolean value to \a is_sequence_embedded. */ void CleavageConfig::setSequenceEmbedded(bool is_sequence_embedded) { m_sequenceEmbedded = is_sequence_embedded; } /*! \brief Returns the sequence embedded boolean value. */ bool CleavageConfig::isSequenceEmbedded() const { return m_sequenceEmbedded; } //////////////// OPERATORS ///////////////////// /*! \brief Returns true if this CleavageConfig instance and \a other are identical, false otherwise. \sa operator!=() */ bool CleavageConfig::operator==(const CleavageConfig &other) const { if(&other == this) return true; if(CleavageAgent::operator!=(other) || m_partials != other.m_partials || m_startIonizeLevel != other.m_startIonizeLevel || m_stopIonizeLevel != other.m_stopIonizeLevel || m_sequenceEmbedded != other.m_sequenceEmbedded) return false; return true; } /*! \brief Returns true if this instance and \a other differ, false otherwise. Returns the negative value of opearator==(). \sa operator==() */ bool CleavageConfig::operator!=(const CleavageConfig &other) const { return !operator==(other); } void CleavageConfig::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register CleavageConfig class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&CleavageConfig::staticMetaObject); engine->globalObject().setProperty("CleavageConfig", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CleavageMotif.cpp000664 001750 001750 00000044254 15100504560 025506 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CleavageMotif.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CleavageMotif \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CleavageMotif.hpp \brief The CleavageMotif class provides a model for specifying aqueous cleavage motfis of \l{Polymer} \l{Sequence}s. When a polymer sequence cleavage occurs, using, for example, the Trypsin cleavage agent, that cleavage agent specifies that cleavage should occur at the following sites "Lys/;Arg/;-Lys/Pro". The "Lys/;Arg/;-Lys/Pro" string of sites gets parsed and \e{cleavage motifs} are generated from it. In this specific case, we'll have three \e{CleavageMotif} instances with the following data: - First motif (or cleavage site): - "Lys" - monomer container: [0] = "Lys" - offset = 1 ('/' indicates that the cut is right of monomer) - is for cleavage ? = Enums::CleavageAction::CLEAVE - Second motif (or cleavage site): - "Arg" - monomer container: [0] = "Arg" - offset = 1 ('/' indicates that the cut is right of monomer) - is for cleavage ? = Enums::CleavageAction::CLEAVE - Third motif (or non-cleavage site): - "-LysPro" - monomer container: [0] = "Lys", [1] = "Pro" - offset = 1 ('/' indicates that the cut is right of monomer) - is for cleavage ? = Enums::CleavageAction::NO_CLEAVE Thanks to this deconstruction (from "Lys/;Arg/;-Lys/Pro" to the 3 cleavage motifs above) is the polymer sequence cleaved according to the cleavage agent specification. \sa CleavageAgent, CleavageRule */ /*! \variable MsXpS::libXpertMassCore::CleavageMotif::mcsp_polChemDef \brief The \l PolChemDef (polymer chemistry definition) that is needed. */ /*! \variable MsXpS::libXpertMassCore::CleavageMotif::m_monomers \brief The container of Monomer instances. The Monomer instances are in the form of const shared pointers to Monomer in the polymer chemistry definition. A "Lys/Pro" motif will translate into two Monomer instances (actually pointers) having codes Lys and Pro, in the right order. */ /*! \variable MsXpS::libXpertMassCore::CleavageMotif::m_offset \brief The offset between the actual cleavage site and the first monomer of the motif. In the The "Lys/", "Arg/" and "-Lys/Pro" examples, the offset would be 1 for each motif, because each time the cleavage occurs after the first monomer code: Lys/, or Arg/ or Lys/Pro. In the /Asp cleavage site the offset would be 0, because the cleavage occurs before the first monomer in the motif (Asp). In the ATGC/GCAT cleavage motif, the offset would be 4. */ /*! \variable MsXpS::libXpertMassCore::CleavageMotif::m_cleavageAction \brief Tells if the motif is for cleavage or not for cleavage. In the "Lys/" and "Arg/" motif, that would be true; for "-Lys/Pro", that would be false, because Trypsin does not cleave after a Lysyl residue if it is followed by a Prolyl residue. */ /*! \variable MsXpS::libXpertMassCore::CleavageMotif::m_isValid \brief Tell the validity status of the CleavageMotif instance. */ /*! \brief Constructs a cleavage motif with a number of parameters. \list \li \a pol_chem_def_csp Polymer chemistry definition. Cannot be nullptr. \li \a motif Motif in the form of "Lys/" or "Lys/Pro" or "/Asp". \li \a offset Offset position of the cleavage to the first monomer code in the motif. \li \a cleavage_action Tells if motif is for cleavage (for example, "Lys/") or not for cleavage (for example, "-Lys/Pro"). \endlist After setting the member data, the CleavageMotif instance is validated and m_isValid is set to the result of this validation. */ CleavageMotif::CleavageMotif(PolChemDefCstSPtr pol_chem_def_csp, const QString &motif, int offset, Enums::CleavageAction cleavage_action) : mcsp_polChemDef(pol_chem_def_csp), m_offset(offset), m_cleavageAction(cleavage_action) { if(!parseMotif(motif)) qCritical() << "Upon construction of CleavageMotif, the motif could not be parsed."; // Let's see the other members. ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of CleavageMotif, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a CleavageMotif instance as a copy of \a other. After setting the member data, the CleavageMotif instance is validated and m_isValid is set to the result of this validation. */ CleavageMotif::CleavageMotif(const CleavageMotif &other) : mcsp_polChemDef(other.mcsp_polChemDef), m_offset(other.m_offset), m_cleavageAction(other.m_cleavageAction) { m_monomers = other.m_monomers; // Let's see the other members. ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon copy-construction of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destructs this CleavageMotif instance. */ CleavageMotif::~CleavageMotif() { m_monomers.clear(); } /*! \brief Sets the PolChemDef member to \a pol_chem_def_csp. This instance then undergoes validation and m_isValid is set to the result of it. */ void CleavageMotif::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting PolChemDef of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the \l PolChemDef. */ PolChemDefCstSPtr CleavageMotif::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } /*! \brief Parses the \a motif and fills-in the member container of Monomers as a result of the parsing. \sa parseMotif() */ void CleavageMotif::setMotif(const QString &motif) { if(!parseMotif(motif)) { qCritical() << "Upon setting motif string of CleavageMotif, the motif could " "not be parsed."; m_isValid = false; } // Let's see the other members. ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting motif of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the motif as a concatenation of the codes of the Monomer instances found in the member container of Monomers. */ QString CleavageMotif::getMotif() const { QString text; for(const MonomerSPtr &monomer_csp : m_monomers) text += monomer_csp->getCode(); return text; } /*! \brief Returns a const reference to the container of Monomers. */ const std::vector & CleavageMotif::getMonomersCstRef() const { return m_monomers; } /*! \brief Returns a reference to the container of Monomers. */ std::vector & CleavageMotif::getMonomersRef() { return m_monomers; } /*! \brief Sets the \a offset. The offset is the position of the cleavage inside the motif, with reset to the first monomer code in that motif. For example, for a cleavage site "Lys/Pro", the cleavage motif becomes {"Lys", "Pro"} and the offset is 1, while for a site "/Asp", the cleavage motif becomes {"Asp"} and the offset is 0. An offset value can thus not be greater than the number of Monomer codes in the motif. This instance then undergoes validation and m_isValid is set to the result of it. */ void CleavageMotif::setOffset(int offset) { m_offset = offset; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting offset of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the offset. */ int CleavageMotif::getOffset() const { return m_offset; } /*! \brief Sets the member Enums::CleavageAction to \a cleavage_action. This instance then undergoes validation and m_isValid is set to the result of it. */ void CleavageMotif::setCleavageAction(Enums::CleavageAction cleavage_action) { m_cleavageAction = cleavage_action; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting cleave operation of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns if motif is for cleavage or not. */ Enums::CleavageAction CleavageMotif::getCleavageAction() const { return m_cleavageAction; } /*! \brief Assigns \a other to this CleavageMotif instance. This instance then undergoes validation and m_isValid is set to the result of it. Returns a reference to this CleavageMotif instance. */ CleavageMotif & CleavageMotif::operator=(const CleavageMotif &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_offset = other.m_offset; m_cleavageAction = other.m_cleavageAction; m_monomers = other.m_monomers; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment of CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if \c this and \a other are identical. The comparison of the Monomer instances in the member container is deep (that is the Monomer instances are compared and not the shared pointers). */ bool CleavageMotif::operator==(const CleavageMotif &other) const { if(&other == this) return true; // Remember that the motif is actually a vector of MonomerSPtr, so when one // does a copy either via the operator ==() or the copy constructor, the // Motifs are going to be the same. // If, then one of the MonomerSPtr is changed, the other is changed also. // qDebug() << "Now == comparing cleavage motifs:" << this->getMotif() // << "versus" << other.getMotif(); // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). if(m_offset != other.m_offset || m_monomers.size() != other.m_monomers.size() || m_cleavageAction != other.m_cleavageAction) { // qDebug() << "Either or both offset or/and cleavage operation differ."; return false; } for(std::size_t iter = 0; iter < m_monomers.size(); ++iter) { if(*m_monomers.at(iter).get() != *other.m_monomers.at(iter).get()) { // qDebug() << "At least one Monomer instance differ in both // CleavageMotif instances."; return false; } } return true; } /*! \brief Returns true if \c this and \a other are different. Returns the negated result of operator==(). */ bool CleavageMotif::operator!=(const CleavageMotif &other) const { if(&other == this) return false; return !operator==(other); } /*! \brief Parses the cleavage \a site and returns the count of Monomer instances stored in the member container as a result of parsing the \a site. A cleavage site is a string in the form "Lys/Pro" or "/Asp". The member container of Monomer pointers is filled-in with pointers to the PolChemDef's Monomer instances having codes {Lys, Pro} or {Asp}. The offset is the position of the '/' cleavage symbol (that is, the actual cleavage position in the motif). For "Lys/Pro", the offset is 1 while for "/Asp", the offset is 0. After setting the member data, the CleavageMotif instance is validated and m_isValid is set to the result of this validation. */ std::size_t CleavageMotif::parseSite(const QString &site) { m_monomers.clear(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "Cannot parse cleavage site without any available PolChemDef."; m_isValid = false; return 0; } if(site.count('/') == 0 || site.count('/') > 1) { qCritical() << "Cannot parse cleavage site: either missing '/' cleavage " "indicator or more than one."; m_isValid = false; return 0; } std::size_t code_length = mcsp_polChemDef->getCodeLength(); std::size_t monomer_count = 0; QRegularExpression single_monomer_code_regexp( QString("([/]?)([A-Z][a-z]{0,%1})([/]?)").arg(code_length - 1)); for(const QRegularExpressionMatch &match : single_monomer_code_regexp.globalMatch(site)) { QString sub_match = match.captured(0); // qDebug() << "Entering single_monomer_code_regexp sub-match:" << // sub_match; QString ant_cleave = match.captured(1); // qDebug() << "ant_cleave:" << ant_cleave; QString code = match.captured(2); // qDebug() << "Parsed code:" << code; QString post_cleave = match.captured(3); // qDebug() << "post_cleave:" << post_cleave; if(!ant_cleave.isEmpty()) m_offset = monomer_count; if(code.isEmpty()) qFatalStream() << "Programming error: code cannot be empty."; const MonomerSPtr monomer_csp = mcsp_polChemDef->getMonomerCstSPtrByCode(code); if(monomer_csp == nullptr) qFatalStream() << "Programming error. The monomer code must be known to the " "PolChemDef."; m_monomers.push_back(monomer_csp); ++monomer_count; if(!post_cleave.isEmpty()) m_offset = monomer_count; } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon parsing cleavage site in CleavageMotif, the instance " "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); return m_monomers.size(); } /*! \brief Parses the cleavage \a motif and returns the count of Monomer instances stored in m_monomers as a result of parsing the \a motif. A cleavage motif is a string in the form "LysPro" or "Asp" (in fact that is the string representation of a \l{Sequence}). */ std::size_t CleavageMotif::parseMotif(const QString &motif) { m_monomers.clear(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "Cannot parse motif without any available PolChemDef;"; m_isValid = false; return 0; } Sequence motif_sequence(mcsp_polChemDef, motif); ErrorList error_list; if(!motif_sequence.validate(&error_list)) { qCritical() << "The motif" << motif << "failed to convert to a valid Sequence object, with errors:" << Utils::joinErrorList(error_list, ", "); m_isValid = false; return 0; } motif_sequence.cleanupMonomers(); for(const MonomerSPtr &monomer_sp : motif_sequence.getMonomersCstRef()) { m_monomers.push_back(monomer_sp); // qDebug() << "Pushed back monomer:" << monomer_csp->toString(); } return m_monomers.size(); } /*! \brief Returns true if validation of this CleavageMotif instance was successful, false otherwise. If errors are encountered and \a error_list_p is not nullptr, then these error are stored in that ErrorList. */ bool CleavageMotif::validate(ErrorList *error_list_p) const { m_isValid = true; qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "A CleavageMotif with no PolChemDef available cannot " "validate successfully."; if(error_list_p != nullptr) error_list_p->push_back( "A CleavageMotif with no PolChemDef available cannot validate " "successfully"); } if(m_monomers.size() == 0) { qCritical() << "A CleavageMotif with no Monomer motif cannot validate " "successfully."; if(error_list_p != nullptr) error_list_p->push_back( "A CleavageMotif with no Monomer motif cannot validate successfully"); } if(m_offset > m_monomers.size()) { qCritical() << "A CleavageMotif with an offset greater than the number of " "Monomer codes in the motif cannot validate successfully."; if(error_list_p != nullptr) error_list_p->push_back( "A CleavageMotif with an offset greater than the number of " "Monomer codes in the motif cannot validate successfully"); } if(m_cleavageAction == Enums::CleavageAction::NOT_SET) { qCritical() << "A CleavageMotif with no set Enums::CleavageAction cannot " "validate successfully."; if(error_list_p != nullptr) error_list_p->push_back( "A CleavageMotif with no set Enums::CleavageAction cannot validate " "successfully"); } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /* \brief Returns the validity status of this CleavageMotif instance. */ bool CleavageMotif::isValid() const { return m_isValid; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CleavageRule.cpp000664 001750 001750 00000051206 15100504560 025332 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CleavageRule.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CleavageRule \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CleavageRule.hpp \brief The CleavageRule class provides a model for specifying aqueous cleavage rules for refining cleavage agent specifications (\l CleavageAgent) of \l{Polymer} \l{Sequence}s. Cleavage rules help refine the description of the chemical reaction that is the basis of a cleavage (either enzymatic or chemical). While a number of cleavage agents (like a number of enzymes) do not make unexpected reactions upon the cleavage (enzymes usually hydrolyze their substrates), there are chemical agents that during the process of cleaving their polymer sequence substrate also chemically modify the ends of the generated oligomers. One notorious example is the case of cyanogen bromide, that cleaves proteins right (that is, C-terminal) of methionyl residues. Upon such cleavage, the monomer at the right side of the generated oligomer (methionyl residue) gets modified according to this actionformula: "-CH2S+O". This reaction is modelled using a CleavageRule. \sa CleavageMotif, CleavageAgent */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_name \brief The name of the CleavageRule. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_leftCode \brief The \l Monomer code at the left of the cleavage site. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_leftFormula \brief The \l Formula to be applied onto the left monomer code. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_rightCode \brief The \l Monomer code at the right of the cleavage site. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_rightFormula \brief The \l Formula to be applied onto the right monomer code. */ /*! \variable MsXpS::libXpertMassCore::CleavageRule::m_isValid \brief The validity status of the CleavageRule instance. */ /*! \brief Constructs a CleavageRule instance \list \li \a pol_chem_def_csp: Polymer chemistry definition. \li \a name: the name. \li \a left_code: The \l Monomer code at the left of the cleavage site. \li \a left_formula: .The \l Formula to be applied onto the left monomer code. \li \a right_code: .The \l Monomer code at the right of the cleavage site. \li \a right_formula: .The \l Formula to be applied onto the right monomer code. \endlist After setting the member data, the instance is validated and the result is set to m_isValid. */ CleavageRule::CleavageRule(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &left_code, const QString &left_formula, const QString &right_code, const QString &right_formula) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_leftCode(left_code), m_leftFormula(left_formula), m_rightCode(right_code), m_rightFormula(right_formula) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a CleavageRule instance as a copy of \a other. After setting the member data, the instance is validated and the result is set to m_isValid. */ CleavageRule::CleavageRule(const CleavageRule &other) : mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_leftCode(other.m_leftCode), m_leftFormula(other.m_leftFormula), m_rightCode(other.m_rightCode), m_rightFormula(other.m_rightFormula) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon copy-construction of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destructs this CleavageRule instance */ CleavageRule::~CleavageRule() { } /*! \brief Sets the PolChemDef to \a pol_chem_def_csp. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting PolChemDef of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the PolChemDef. */ PolChemDefCstSPtr CleavageRule::getPolchemDefCstSPtr() const { return mcsp_polChemDef; } /*! \brief Sets the name to \a name. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting name of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ const QString & CleavageRule::getName() const { return m_name; } /*! \brief Sets the left \a code. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setLeftCode(const QString &code) { m_leftCode = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting left code of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the left code. */ const QString & CleavageRule::getLeftCode() const { return m_leftCode; } /*! \brief Sets the right \a code. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setRightCode(const QString &code) { m_rightCode = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting right code of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the right code. */ const QString & CleavageRule::getRightCode() const { return m_rightCode; } /*! \brief Sets the left \a formula. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setLeftFormula(const Formula &formula) { m_leftFormula = formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting left formula of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the left formula. */ const Formula & CleavageRule::getLeftFormula() const { return m_leftFormula; } /*! \brief Sets the right \a formula. After setting the member data, the instance is validated and the result is set to m_isValid. */ void CleavageRule::setRightFormula(const Formula &formula) { m_rightFormula = formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting right formula of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the right formula. */ const Formula & CleavageRule::getRightFormula() const { return m_rightFormula; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns to \a other to this CleavageRule instance. After setting the member data, the instance is validated and the result is set to m_isValid. Returns a reference to this CleavageRule instance. */ CleavageRule & CleavageRule::operator=(const CleavageRule &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_leftCode = other.m_leftCode; m_leftFormula = other.m_leftFormula; m_rightCode = other.m_rightCode; m_rightFormula = other.m_rightFormula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment of CleavageRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if \c this and \a other are identical. */ bool CleavageRule::operator==(const CleavageRule &other) const { if(this == &other) return true; // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). return m_name == other.m_name && m_leftCode == other.m_leftCode && m_leftFormula == other.m_leftFormula && m_rightCode == other.m_rightCode && m_rightFormula == other.m_rightFormula; } /*! \brief Returns true if \c this and \a other are different. */ bool CleavageRule::operator!=(const CleavageRule &other) const { if(this == &other) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Validates this CleavageRule instance and stores error messages to \a error_list_p. Validation entails the following: \list \li The member PolChemDef cannot be nullptr; \li The member name cannot be empty; \li If the left monomer code is not empty, it must be known to the polymer chemistry definition. In that case, if the left formula is not empty, it needs to validate successfully. \li The same logic is applied to the monomer at the right hand side of the cleavage site. \endlist Sets m_isValid to true if the validation is successful, false otherwise. Returns m_isValid. */ bool CleavageRule::validate(ErrorList *error_list_p) const { m_isValid = false; qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "A CleavageRule with no PolChemDef available cannot " "validate successfully."; error_list_p->push_back( "A CleavageRule with no PolChemDef available cannot validate " "successfully"); } if(m_name.isEmpty()) { qCritical() << "A CleavageRule with no name cannot validate successfully."; error_list_p->push_back( "A CleavageRule with no name cannot validate successfully"); } if(m_leftCode.isEmpty() && !m_leftFormula.getActionFormula().isEmpty()) { qCritical() << "A CleavageRule without a left monomer code but with a left formula" "cannot validate successfully."; error_list_p->push_back( "A CleavageRule without a left monomer code but with a left formula " "cannot validate " "successfully"); } if(!m_leftCode.isEmpty()) { if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && mcsp_polChemDef->getMonomerCstSPtrByCode(m_leftCode) == nullptr) { qCritical() << "A CleavageRule with an unknown left monomer code " "cannot validate successfully."; error_list_p->push_back( "A CleavageRule with an unknown left monomer code cannot validate " "successfully"); } if(m_leftFormula.getActionFormula().isEmpty()) { qCritical() << "A CleavageRule with an empty left formula cannot " "validate successfully."; error_list_p->push_back( "A CleavageRule with an empty left formula cannot validate " "successfully"); } else if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_leftFormula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "A CleavageRule with an invalid left formula cannot " "validate successfully."; error_list_p->push_back( "A CleavageRule with an invalid left formula cannot validate " "successfully"); } } if(m_rightCode.isEmpty() && !m_rightFormula.getActionFormula().isEmpty()) { qCritical() << "A CleavageRule without a right monomer code but with a " "right formula" "cannot validate successfully."; error_list_p->push_back( "A CleavageRule without a right monomer code but with a right formula " "cannot validate " "successfully"); } if(!m_rightCode.isEmpty()) { if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && mcsp_polChemDef->getMonomerCstSPtrByCode(m_rightCode) == nullptr) { qCritical() << "A CleavageRule with an unknown right monomer code " "cannot validate successfully."; error_list_p->push_back( "A CleavageRule with an unknown right monomer code cannot validate " "successfully"); } if(m_rightFormula.getActionFormula().isEmpty()) { qCritical() << "A CleavageRule with an empty right formula cannot " "validate successfully."; error_list_p->push_back( "A CleavageRule with an empty right formula cannot validate " "successfully"); } else if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_rightFormula.validate( mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "A CleavageRule with an invalid right formula cannot " "validate successfully."; error_list_p->push_back( "A CleavageRule with an invalid right formula cannot validate " "successfully"); } } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this CleavageRule. */ bool CleavageRule::isValid() const { return m_isValid; } //////////////// XML DATA LOADING WRITING ///////////////////// /*! \brief Parses the CleavageRule XML \a element using a \a{version}ed function. Upon parsing of the \a element, its data are validated and set to this CleavageRule instance, thus essentially initializing it. Returns true if parsing and validation were successful, false otherwise. */ bool CleavageRule::renderXmlClrElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; bool leftCodeSet = false; bool leftFormulaSet = false; bool rightCodeSet = false; bool rightFormulaSet = false; /* The xml node we are in is structured this way: * * * Homeseryl * M * -C1H2S1+O1 * M * -C1H2S1+O1 * * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that xml_node->name == "clr" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. * * Note that the DTD stipulates that there can be no or one at most * of each left end and/or right end set of data. So be careful * with the assertions ! * This is the DTD material: * */ if(element.tagName() != "clr") return false; child = element.firstChildElement(); if(version == 1) { // no-op version = 1; } if(child.tagName() != "name") return false; m_name = child.text(); child = child.nextSiblingElement(); while(!child.isNull()) { // OK, apparently there is a child element, so let's try to see // what's going on. It can either be "le-mnm-code" or "re-mnm-code". if(child.tagName() == "le-mnm-code") { m_leftCode = child.text(); leftCodeSet = true; } else if(child.tagName() == "le-formula") { m_leftFormula.setActionFormula(child.text()); leftFormulaSet = true; } else if(child.tagName() == "re-mnm-code") { m_rightCode = child.text(); rightCodeSet = true; } else if(child.tagName() == "re-formula") { m_rightFormula.setActionFormula(child.text()); rightFormulaSet = true; } child = child.nextSiblingElement(); } // OK, we just finished parsing this element. Check what we // got. if(leftCodeSet) { if(!leftFormulaSet) return false; } if(rightCodeSet) { if(!rightFormulaSet) return false; } // It cannot be that no single code could be set. if(!leftCodeSet && !rightCodeSet) return false; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The CleavageRule rendered from element is invalid."; } qDebug() << "The element was rendered successfully."; return m_isValid; } /*! \brief Formats a string representing this CleavageRule instance suitable to use as an XML element. The typical cleavage rule element that is generated in this function looks like this: \code M -CH2S+O \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a string. */ QString CleavageRule::formatXmlClrElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* M -CH2S+O */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); if(!m_leftCode.isEmpty()) { Q_ASSERT(!m_leftFormula.getActionFormula().isEmpty()); text += QString("%1%2\n").arg(lead).arg(m_leftCode); text += QString("%1%2\n") .arg(lead) .arg(m_leftFormula.getActionFormula()); } if(!m_rightCode.isEmpty()) { Q_ASSERT(!m_rightFormula.getActionFormula().isEmpty()); text += QString("%1%2\n").arg(lead).arg(m_rightCode); text += QString("%1%2\n") .arg(lead) .arg(m_rightFormula.getActionFormula()); } // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Cleaver.cpp000664 001750 001750 00000177157 15100504560 024372 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Cleaver.hpp" #include "MsXpS/libXpertMassCore/CrossLink.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Cleaver \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile Cleaver.hpp \brief The Cleaver class provides a model for performing aqueous cleavage reactions involving \l{CleavageAgent} objects and \l{Polymer} \l{Sequence}s. \sa CleavageAgent, CleavageMotif, CleavageConfig, Ionizer */ /*! \variable MsXpS::libXpertMassCore::Cleaver::mcsp_polymer \brief The \l Polymer polymer that is being cleaved (digested). */ /*! \variable MsXpS::libXpertMassCore::Cleaver::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_cleavageConfig \brief The CleavageConfig that configures the cleavage reaction. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_calcOptions \brief The CalcOptions that configure the way masses and formulas are to be computed. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_ionizer \brief The Ionizer that directs the ionization of the Oligomer instances obtained by cleaving the Polymer. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_doCleaveIndices \brief The vector of indices in the Polymer Sequence that are \e{indeed} the site of cleavage. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_doNotCleaveIndices \brief The vector of indices in the Polymer Sequence that are \e{not} the site of cleavage. */ /*! \variable MsXpS::libXpertMassCore::Cleaver::m_oligomers \brief The vector of Oligomer instances generated as a result of the cleavage. */ /*! \brief Constructs an entirely empty Cleaver instance. */ Cleaver::Cleaver() { } /*! \brief Constructs a Cleaver instance with a number of parameters. \list \li \a polymer_cqsp The Polymer instance to be cleaved. \li \a pol_chem_def_csp The PolChemDef (polymer chemistry definition) that is the context in which the Polymer exists. \li \a cleavage_config The CleavageConfig instance that configures the cleavage. \li \a calc_options The CalcOptions instance that configures the mass and formula calculations. \li \a ionizer The Ionizer instance that drives the ionization of the Oligomer instances generated by the cleavage. \endlist If polymer_cqsp or pol_chem_def_csp is nullptr, that is a fatal error. */ Cleaver::Cleaver(PolymerCstQSPtr polymer_cqsp, PolChemDefCstSPtr pol_chem_def_csp, const CleavageConfig &cleavage_config, const CalcOptions &calc_options, const Ionizer &ionizer) : mcsp_polymer(polymer_cqsp), mcsp_polChemDef(pol_chem_def_csp), m_cleavageConfig(cleavage_config), m_ionizer(ionizer) { if(mcsp_polymer == nullptr && mcsp_polymer.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(mcsp_polChemDef == nullptr && mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; m_calcOptions.initialize(calc_options); // qDebug() << "Other calc options:" << calc_options.toString(); // qDebug() << "And now *this calc options:" << m_calcOptions.toString(); // qDebug() << "Instantiating Cleave with Ionizer:" << m_ionizer.toString(); // qDebug() << "Ionizer' isotopic data:" // << m_ionizer.getIsotopicDataCstSPtr()->size(); } /*! \brief Constructs Cleaver instance as a copy of \a other. If polymer_cqsp or pol_chem_def_csp is nullptr, that is a fatal error. */ Cleaver::Cleaver(const Cleaver &other) : mcsp_polymer(other.mcsp_polymer), mcsp_polChemDef(other.mcsp_polChemDef), m_cleavageConfig(other.m_cleavageConfig), m_ionizer(other.m_ionizer) { if(mcsp_polymer == nullptr && mcsp_polymer.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(mcsp_polChemDef == nullptr && mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; m_calcOptions.initialize(other.m_calcOptions); // qDebug() << "Other calc options:" << other.m_calcOptions.toString(); // qDebug() << "And now *this calc options:" << m_calcOptions.toString(); // qDebug() << "Instantiating Cleave with Ionizer:" << m_ionizer.toString(); } /*! \brief Desstructs this Cleaver instance */ Cleaver::~Cleaver() { } /*! \brief Returns the CleavageAgent name (from the member CleavageConfig instance's base class). */ QString Cleaver::getCleaveAgentName() const { return m_cleavageConfig.getName(); } /*! \brief Transfers (using std::move()) all the Oligomer instances from \a source to \a dest. After the transfer, the \a source Oligomer container is cleared since it contains only nullptr items. */ std::size_t Cleaver::transferOligomers(OligomerCollection &source, OligomerCollection &dest) { std::size_t dest_oligomer_count_before = dest.getOligomersRef().size(); // Move each element from source to dest dest.getOligomersRef().insert( dest.getOligomersRef().end(), std::make_move_iterator(source.getOligomersRef().begin()), std::make_move_iterator(source.getOligomersRef().end())); std::size_t dest_oligomer_count_after = dest.getOligomersRef().size(); std::size_t transferred_count = dest_oligomer_count_after - dest_oligomer_count_before; // Sanity check if(transferred_count != source.getOligomersRef().size()) qFatalStream() << "Programming error. Not all the Oligomers were transferred."; // Now clear the source container which contains the same items as before but // all the shared pointers are now nullptr. source.getOligomersRef().clear(); return transferred_count; } /*! \brief Returns a const reference to the member OligomerCollection instance. */ const OligomerCollection & Cleaver::getOligomerCollectionCstRef() const { return m_oligomers; } /*! \brief Returns a reference to the member OligomerCollection instance. */ OligomerCollection & Cleaver::getOligomerCollectionRef() { return m_oligomers; } /*! \brief Performs the actual cleavage, thus generating Oligomer instances that are added to the member OligomerCollection instance. If \a reset is true, the member OligomerCollection is first cleared; otherwise the newly generated Oligomer instances are simply added to it. Returns true upon success, false otherwise. */ bool Cleaver::cleave(bool reset) { // If the polymer sequence is empty, just return. if(!mcsp_polymer->size()) { qCritical() << "The polymer sequence is empty: nothing to cleave."; return true; } // Ensure that the cleavage pattern was already parsed. if(!m_cleavageConfig.getMotifsCstRef().size()) { if(!m_cleavageConfig.parse()) { qCritical() << "Failed to parse the cleavage options"; return false; } } // qDebug() << "Number of motifs:" // << m_cleavageConfig.getMotifsCstRef().size(); if(!fillCleavageIndices()) { // qDebug() << "Index lists(cleave/nocleave) are empty." // "No oligomer generated."; // We can return true, as no error condition was found but not // oligomers were generated. return true; } if(!resolveCleavageNoCleavage()) { qDebug() << "There are no cleavage indices left. Nothing to do."; return false; } if(!removeDuplicateCleavageIndices()) { qDebug() << "There are no cleavage indices left. Nothing to do."; return false; } if(reset) m_oligomers.clear(); for(int iter = 0; iter <= m_cleavageConfig.getPartials(); ++iter) { // qDebug() << "Now performing partial cleavage" << iter; if(cleavePartial(iter) == -1) { qCritical() << "Failed to perform partial cleavage" << iter; return false; } } // At this point we have the list of lists of oligomers, one list of // oligomers for each partial cleavage. m_doCleaveIndices.clear(); m_doNotCleaveIndices.clear(); return true; } /*! \brief Fills-in all the index values that correspond to precise locations where the cleavage reactions must occur. These indices represent location in the member \l{Polymer} \l{Sequence}. If the pattern only contains cleaving sites, all the indices are added to the member container of cleavage indices. If the pattern contains also no-cleaving sites (like with Trypsin's "-Lys/Pro" pattern), then the corresponding indices are set to the member container of no-cleavage indices. Returns the sum of the two cleavage/no-cleavage containers sizes. Or 0 if not a single cleavage site was found in the Polymer Sequence. */ int Cleaver::fillCleavageIndices() { const std::vector &cleavage_motifs = m_cleavageConfig.getMotifsCstRef(); m_doCleaveIndices.clear(); m_doNotCleaveIndices.clear(); // The cleavage might be performed on a selected portion of a sequence only, // not necessarily on the whole polymer sequence. // qDebug() << "The calculation options:" << m_calcOptions.toString(); IndexRangeCollection index_range_collection( m_calcOptions.getIndexRangeCollectionCstRef()); // qDebug() << "The index range collection:" // << index_range_collection.indicesAsText(); std::size_t index_start; std::size_t index_stop; if(!index_range_collection.size()) { index_start = 0; index_stop = mcsp_polymer->size(); } else { index_start = index_range_collection.getRangeCstRefAt(0).m_start; index_stop = index_range_collection.getRangeCstRefAt(0).m_stop; } // qDebug() << "index_start:" << index_start << "index_stop:" << index_stop; for(const CleavageMotifSPtr &cleavage_motif_sp : cleavage_motifs) { int index = index_start; while(1) { ++index; index = findCleavageMotif(*cleavage_motif_sp, index, index_stop); if(index == -1) break; // Do not forget: The position at which the motif is found // in the polymer sequence is not necessarily the position // at which the cleavage will effectively occur. Indeed, // let's say that we found such motif in the polymer // sequence: "KKRKGP". This motif was extracted from a // cleavage agent that had a pattern like this: "KKRK/GP". What // we see here is that the cleavage occurs after the fourth // monomer! And we must realize that the 'index' returned // above corresponds to the index of the first 'K' in // "KKRKGP" motif that was found in the polymer // sequence. Thus we have to take into account the offset //(+4, in our example, WHICH IS A POSITION and not an // index, which is why we need to remove 1 below) of the // cleavage: int actual_cleave_index = index + cleavage_motif_sp->getOffset() - 1; if(actual_cleave_index < 0) continue; if(actual_cleave_index >= static_cast(index_stop)) break; // qDebug() << __FILE__ << __LINE__ // << "Found new cleavage index:" // << actual_cleave_index; if(cleavage_motif_sp->getCleavageAction() == Enums::CleavageAction::CLEAVE) { m_doCleaveIndices.push_back(actual_cleave_index); // qDebug() << __FILE__ << __LINE__ // << "For cleavage, index:" << actual_cleave_index; } else if(cleavage_motif_sp->getCleavageAction() == Enums::CleavageAction::NO_CLEAVE) { m_doNotCleaveIndices.push_back(actual_cleave_index); // qDebug() << __FILE__ << __LINE__ // << "Not for cleavage"; } else qFatalStream() << "Programming error. Enums::CleavageAction::NOT_SET is not " "possible here."; } // End of // while (1) } // End of // for (int iter = 0; iter < cleavage_motifs->size(); ++iter) // Note that returning 0 is not an error condition, because a // sequence where no site is found whatsoever will result in 0. return m_doCleaveIndices.size() + m_doNotCleaveIndices.size(); } /*! \brief Removes form the container of cleavage indices all the indices that were found to be no-cleavage indices in the corresponding container. Returns the size of the container of cleavage indices. */ int Cleaver::resolveCleavageNoCleavage() { // Remove from the m_cleaveIndices container all the indices // that are found in the m_noCleaveIndices vector. for(const int &no_cleave_index : m_doNotCleaveIndices) { std::vector::iterator the_iterator = m_doCleaveIndices.begin(); // The erase() below works because in the while statement we // do test for the end() of the vector instead of determining // that end once and storing it in a variable. while(the_iterator != m_doCleaveIndices.end()) { if((*the_iterator) == no_cleave_index) the_iterator = m_doCleaveIndices.erase(the_iterator); else ++the_iterator; } } #if 0 #Old version for(int iter = 0; iter < m_noCleaveIndices.size(); ++iter) { int noCleaveIndex = m_noCleaveIndices.at(iter); for(int jter = 0; jter < m_cleaveIndices.size(); ++jter) { int cleaveIndex = m_cleaveIndices.at(jter); if(noCleaveIndex == cleaveIndex) m_cleaveIndices.removeAt(jter); } } #endif return m_doCleaveIndices.size(); } /*! \brief Removes the duplicate cleavage indices from the container of cleavage indices. */ int Cleaver::removeDuplicateCleavageIndices() { std::sort(m_doCleaveIndices.begin(), m_doCleaveIndices.end()); auto last = std::unique(m_doCleaveIndices.begin(), m_doCleaveIndices.end()); m_doCleaveIndices.erase(last, m_doCleaveIndices.end()); return m_doCleaveIndices.size(); } /*! \brief Returns an index at which the \a cleavage_motif CleavageMotif is found in the Polymer Sequence. The search is started at index \a index_start and is stopped at index \a index_stop. If \a cleavage_motif is not found, returns -1. */ int Cleaver::findCleavageMotif(CleavageMotif &cleavage_motif, std::size_t index_start, std::size_t index_stop) { // qDebug() << "start:" << index_start << "stop:" << index_stop; bool search_failed = false; int first_index = 0; const std::vector &polymer_sequence_monomers = mcsp_polymer->getSequenceCstRef().getMonomersCstRef(); const std::vector &cleavage_motif_monomers = cleavage_motif.getMonomersCstRef(); // We have to iterate in the polymer sequence starting at 'index', in // search for a sequence element identical to the sequence that is represented // in the cleavage_motif in the form of a container of MonomerCstSPtr. // This means that if // cleavage_motif_monomers[0]->getCode() = "Lys" // cleavage_motif_monomers[1]->getCode() = "Pro" // then, we want to search in the polymer_sequence_monomers the same // sequence by iterating in this list from index 'index' onwards, // and we stop searching when the list's end is found or if // list [n] = "Lys" and // list [n+1] = "Pro". if(!mcsp_polymer->size()) return 0; if(!cleavage_motif_monomers.size()) return -1; if(index_stop >= mcsp_polymer->size()) { qFatal() << "Programming error. Index is out of bounds:" << index_stop << "polymer size:" << mcsp_polymer->size(); } // Seed the routine by setting 'first' to the first motif in the // cleavage_motif_monomers (in our example this is "Lys"). QString first_code = cleavage_motif_monomers.front()->getCode(); // And now iterate (starting from 'index') in the polymer // sequence's list of monomers in search for a monomer having the // proper code ("Lys"). std::size_t iter_index = index_start; while(iter_index < index_stop) { MonomerSPtr monomer_sp = polymer_sequence_monomers.at(iter_index); if(monomer_sp == nullptr) qFatalStream() << "Programming error."; if(monomer_sp->getCode() != first_code) { // The polymer sequence currently iterated code is not the one we // search. So go one code further in the polymer sequence. ++iter_index; continue; } // If we are here, then that means that we actually found one // monomer code in the polymer sequence that matches the one of // the cleavage_motif we are looking for. first_index = iter_index; search_failed = false; // Now that we have anchored our search at first_index in the // polymer sequence, continue with next polymer sequence monomer and check // if it matches the next monomer in the cleavage motif that we are // looking for. for(std::size_t iter = 1; iter < cleavage_motif_monomers.size(); ++iter) { if(iter_index + iter >= index_stop) { search_failed = true; break; } QString next_code = cleavage_motif_monomers.at(iter)->getCode(); monomer_sp = polymer_sequence_monomers.at(iter_index + iter); if(monomer_sp == nullptr) qFatalStream() << "Programming error."; if(monomer_sp->getCode() == next_code) continue; else { search_failed = true; break; } } // End of // for (int iter = 1; iter < cleavage_motif_monomers.size(); ++iter) if(search_failed) { ++iter_index; continue; } else { return first_index; } } // End of // while (iter_index < polymer_sequence_monomers.size()) // qDebug() << "At call with start:" << index_start << "stop:" << index_stop // << "now returning -1, with cleavage motif" // << cleavage_motif.getMotif(); return -1; } /*! \brief Accounts into the \a oligomer_sp for the \a cleavage_rule_sp. Returns true if the accounting succeeded, false otherwise. */ bool Cleaver::accountCleavageRule(CleavageRuleSPtr cleavage_rule_sp, OligomerSPtr oligomer_sp) { if(cleavage_rule_sp == nullptr || cleavage_rule_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(oligomer_sp == nullptr || oligomer_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; IsotopicDataCstSPtr isotopic_data_csp = oligomer_sp->getPolymerCstSPtr() ->getPolChemDefCstSPtr() ->getIsotopicDataCstSPtr(); // For each IndexRange element in the oligomer_sp, we have to // ensure we apply the formula(s) that is/are required. // The oligomer_sp might have multiple ranges if it has been crafted as // a cross-linked oligomer. // We need to check the validity of the CleavageRule for each range // individually because each range corresponds to an Oligomer (for example if // this oligomers_p is cross-linked to at least one other oligomer). And each // oligomer might have an end corresponding to the CleavageRule member data // (left or right end). foreach(const IndexRange *item, oligomer_sp->getIndexRangeCollectionCstRef().getRangesCstRef()) { if(!cleavage_rule_sp->getLeftCode().isEmpty()) { // The formula has to be there because the rule has a left code that // is not empty. Formula rule_formula = Formula(cleavage_rule_sp->getLeftFormula()); if(rule_formula.getActionFormula().isEmpty()) qFatalStream() << "Programming error. The cleavage rule's left code is " "non-empty and the left formula thus cannot be empty."; // We are dealing with the cleavage rule's left code/formula, so // check what is the Monomer at the left end of oligomer_sp ? MonomerSPtr monomer_csp = oligomer_sp->getLeftEndMonomerCstSPtr(); if(monomer_csp->getCode() == cleavage_rule_sp->getLeftCode()) { // qDebug() << "Matched left code:" << // cleavage_rule_sp->getLeftCode(); // But, this is not going to be real true, if the // monomer_csp is actually the left-end monomer_csp of the // whole polymer sequence: if this oligomer_sp is actually // the Left-end oligomer (like N-terminal peptide) of the polymer // after having been digested, and the left end monomer of this // oligomer_sp has code equal to the cleavage rule's left code // (which we assessed above), // then the rule has not to be applied because there was no // cleavage at the left end monomer of the polymer! // Checking if the value of sequence_range.start == 0 tells us if // the // oligomer_sp is the left end oligomer of the polymer. If it is // == 0, then we do not apply the cleavage rule because being // the left end oligomer of the polymer, its left end has not // been cleaved. if(!item->m_start) { // The monomer_csp is not the left-end monomer_csp, so the // match is real. Account for the formula ! bool ok = false; rule_formula.accountMasses( ok, isotopic_data_csp, oligomer_sp->getMassRef(Enums::MassType::MONO), oligomer_sp->getMassRef(Enums::MassType::AVG)); if(!ok) return false; oligomer_sp->getFormulaRef().accountFormula( rule_formula.getActionFormula(), isotopic_data_csp, 1, ok); } } } if(!cleavage_rule_sp->getRightCode().isEmpty()) { // The formula has to be there because the rule has a right code that // is not empty. Formula rule_formula = Formula(cleavage_rule_sp->getRightFormula()); // qDebug() << "Right code formula:" // << rule_formula.getActionFormula(); if(rule_formula.getActionFormula().isEmpty()) qFatalStream() << "Programming error. The cleavage rule's right code is " "non-empty and the right formula thus cannot be empty."; // We are dealing with the cleavage rule's right code/formula, so // check what is the Monomer at the right end of oligomer_sp ? MonomerSPtr monomer_csp = oligomer_sp->getRightEndMonomerCstSPtr(); if(monomer_csp->getCode() == cleavage_rule_sp->getRightCode()) { // qDebug() << "Matched right code:" // << cleavage_rule_sp->getRightCode(); // See above for the left end code for detailed explanations. if(item->m_stop != (qsizetype)mcsp_polymer->size() - 1) { // The monomer_csp is not the right-end monomer_csp // of the whole polymer sequence, so the match is real. // Account for the formula ! // qDebug() // << "Before accouting rule formula, mono mass:" // << oligomer_sp->getMass(Enums::MassType::MONO); bool ok = false; rule_formula.accountMasses( ok, isotopic_data_csp, oligomer_sp->getMassRef(Enums::MassType::MONO), oligomer_sp->getMassRef(Enums::MassType::AVG)); if(!ok) { qDebug() << "Accounting masses set ok to false."; return false; } // qDebug() // << "After accouting rule formula, mono mass:" // << oligomer_sp->getMass(Enums::MassType::MONO); // This will modify the formula inside oligomer_sp. // However, calling elementalComposition will not account // for the cleavage rule ! oligomer_sp->getFormulaRef().accountFormula( rule_formula.getActionFormula(), isotopic_data_csp, 1, ok); } } } } return true; } /*! \brief Performs a cleavage operation for partial cleavage \a partial_cleavage. Returns -1 if an error occurred, the count of generated oligomers otherwise. */ int Cleaver::cleavePartial(int partial_cleavage) { bool is_oligomer_the_polymer = false; std::size_t iter = 0; static int left_index = 0; static int right_index = 0; Q_ASSERT(partial_cleavage >= 0); OligomerCollection partial_oligomers; IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // The cleavage might be performed on only a selected portion of the polymer // sequence, not necessarily on the whole polymer sequence. const IndexRange *index_range_p = m_calcOptions.getIndexRangeCollectionCstRef() .mostInclusiveLeftRightIndexRange(); // qDebug() << "Most inclusive left right index range:" // << index_range_p->indicesAsText(); qsizetype index_start = index_range_p->m_start; qsizetype index_stop = index_range_p->m_stop; delete index_range_p; left_index = index_start; right_index = 0; CalcOptions calc_options(m_calcOptions); // Iterate in the container of the indices where the cleavage should occur // in the polymer sequence. for(iter = 0; iter < m_doCleaveIndices.size(); ++iter) { // Make sure, if the partial cleavage is very large, for // example, that it will not lead us to access the polymer // sequence at a monomer index larger than its upper boundary. // Imagine cutting a polymer containing only one Met with // cyanogen bromide: m_cleaveIndices will contain a single // element: the index at which the methionine occurs in the // polymer sequence (and there is a single one). Now, imagine // that we are asked to perform a cleavage with // 'partial_cleavage' of 2. The way we do it is that we fetch the // index in the list of cleavage indices (m_cleaveIndices) two // cleavage positions farther than the position we are iterating into: // int offset_partial_cleavage = iter + partial_cleavage; // Now, if m_cleaveIndices contains a single element, asking // for this m_cleaveIndices.at(iter + partial_cleavage) will // go out of the boundaries of the list, since it has a single // item and partial_cleavage is 2. This is what we are willing to // avoid. std::size_t offset_partial_cleavage = iter + partial_cleavage; if(offset_partial_cleavage >= m_doCleaveIndices.size()) { if(iter == 0) is_oligomer_the_polymer = true; break; } right_index = m_doCleaveIndices.at(offset_partial_cleavage); QString name = QString("%1#%2").arg(partial_cleavage).arg(iter + 1); // Note how we pass Ionizer() below so that it is invalid // because we are not ionizing Oligomer instances right now, // that will come later. calc_options.setIndexRange(left_index, right_index); // qDebug() << "After setting these values to calc_options:" // << calc_options.getIndexRangeCollectionCstRef() // .getRangesCstRef() // .front() // ->indicesAsText(); OligomerSPtr oligomer_sp = std::make_shared( mcsp_polymer, name, m_cleavageConfig.getName(), mcsp_polymer->modifiedMonomerCount( IndexRangeCollection(left_index, right_index)), Ionizer(), calc_options); // qDebug() << "Allocated new oligomer with index range:" // << left_index << "--" << right_index << "calculation options:" // << oligomer_sp->getCalcOptionsCstRef().toString() // << "and default ionizer:" // << oligomer_sp->getIonizerCstRef().toString(); oligomer_sp->setPartialCleavage(partial_cleavage); QString elemental_composition = oligomer_sp->elementalComposition(); // qDebug() << "The elemental composition of this new oligomer as " // "calculated with its own calculation options:" // << calc_options.toString() << "and with its own ionizer:" // << oligomer_sp->getIonizerCstRef().toString() // << "is:" << elemental_composition; // And now use that elemental composition to set it in the Oligomer. bool ok = false; oligomer_sp->getFormulaRef().accountFormula( elemental_composition, isotopic_data_csp, 1, ok); if(!ok) { qWarning() << "Failed to account formula:" << elemental_composition; oligomer_sp.reset(); return -1; } // qDebug() << "The elemental composition above was used to set " // "the oligomer's internal formula, which is now:" // << oligomer_sp->getFormulaRef().getActionFormula(); // At this point we can add the configured oligomer to the list. partial_oligomers.getOligomersRef().push_back(oligomer_sp); // Increment the index for next oligomer. left_index = m_doCleaveIndices.at(iter) + 1; } // End of // for (int iter = 0; iter < m_cleaveIndices.size(); iter=+) // At this point we have finished iterating in the cleave index list, but // there was an oligomer cooking when we ended the looping. We should handle // that stray oligomer exactly the same way we did for the others inside the // loop. // Indeed, this last oligomer that was cooking is the right-end oligomer! And // be sure to determine what's its real left end index ! if(is_oligomer_the_polymer) left_index = index_start; else left_index = m_doCleaveIndices.at(--iter) + 1; // 'iter' is used to construct the name of the oligomer, so we have // to increment it once because we did not have the opportunity to // increment it between the last but one oligomer and this one. ++iter; right_index = index_stop; QString name = QString("%1#%2").arg(partial_cleavage).arg(iter + 1); // Note how we pass Ionizer() below so that it is invalid // because we are not ionizing Oligomer instances right now, // that will come later. calc_options.setIndexRange(left_index, right_index); // qDebug() << "Now creating the last cooking cleavage Oligomer:" // << name << "with indices:" << left_index << "-" << right_index // << "and calculation options:" << calc_options.toString(); OligomerSPtr oligomer_sp = std::make_shared(mcsp_polymer, name, m_cleavageConfig.getName(), mcsp_polymer->modifiedMonomerCount( IndexRangeCollection(left_index, right_index)), Ionizer(), calc_options); oligomer_sp->setPartialCleavage(partial_cleavage); // qDebug() // << "After heap-allocation of Oligomer, its calculation options:" // << oligomer_sp->getCalcOptionsRef().toString(); QString elemental_composition = oligomer_sp->elementalComposition(); // qDebug() << "Elemental composition:" << elemental_composition; // And now use that elemental composition to set it in the Oligomer. bool ok = false; oligomer_sp->getFormulaRef().accountFormula( elemental_composition, isotopic_data_csp, 1, ok); if(!ok) { qWarning() << "Failed to account formula:" << elemental_composition; oligomer_sp.reset(); return -1; } // qDebug() << "Oligomer has formula:" // << oligomer_sp->getFormulaCstRef().getActionFormula(); // At this point we can add the configured oligomer to the list. partial_oligomers.getOligomersRef().push_back(oligomer_sp); // At this point all the skeleton oligomers have been computed for // the given partial_cleavage. We still have to perform the // cross-link analysis prior to both calculate the masses and // perform the ionization of all the generated oligomers. Note that // making cross-link analysis is only useful in case the cleavage is // full (that is, partial_cleavage == 0). if(!partial_cleavage) { if(static_cast(m_calcOptions.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::CROSS_LINKER)) { if(analyzeCrossLinks(partial_oligomers) == -1) { return -1; } } } // Finally, we can now perform the mass calculations and the // ionization. We will use each oligomer in the partial_oligomers // Oligomer container as a template for creating new oligomers (with different // z values) and all the new oligomers will be appended to another Oligomer // container: oligomer_buffer_container. Each time a template Oligomer will // have been used, it will be removed from partial_oligomers. Once all the // Oligomers in partial_oligomers will have been used, and thus removed, all // the newly allocated Oligomer objects in oligomer_buffer_container will be // moved to partial_oligomers. OligomerCollection oligomer_buffer_container; std::vector::iterator partial_oligomers_iterator = partial_oligomers.getOligomersRef().begin(); std::size_t partial_oligomers_count = partial_oligomers.getOligomersRef().size(); // qDebug() << "There are" << partial_oligomers_count // << "items in the container"; // The end iterator needs to be dynamic because we remove the // oligomer after iterating into it. while(partial_oligomers_iterator != partial_oligomers.getOligomersRef().end()) { OligomerSPtr iter_oligomer_sp = (*partial_oligomers_iterator); // qDebug() << "Iterating partial oligomer:" << // iter_oligomer_sp->toString() // << "While there are still" // << partial_oligomers.getOligomersRef().size() // << "items in the container"; // We do not ask that the oligomer be ionized yet, because we // have to first account for potential cleavage rules! Thus we // pass an invalid ionizer with Ionizer(), which is interpreted by // the mass calculation function that ionization should not be // performed. This was a bug in the release versions up // to 1.6.1. // iter_oligomer_sp->calculateMasses(calc_options, Ionizer()); iter_oligomer_sp->calculateMasses(); // At this point we should test if the oligomer has to be // processed using cleavage rules. // qDebug() << "There are:" // << m_cleavageConfig.getRulesCstRef().size() << "cleavage // rules."; for(const CleavageRuleSPtr &cleavage_rule_sp : m_cleavageConfig.getRulesCstRef()) { // qDebug() << "The CleavageRule name:" // << cleavage_rule_sp->getName() // << "Oligomer mono mass BEFORE accounting it:" // << iter_oligomer_sp->getMass(Enums::MassType::MONO) // << "and internal formula is:" // << iter_oligomer_sp->getFormulaCstRef().getActionFormula(); // Note that the accounting of the cleavage rule is // performed as if the oligomer was charged 1. This is why // we have to ionize the oligomer only after we have // completed the determination of its atomic composition. if(!accountCleavageRule(cleavage_rule_sp, iter_oligomer_sp)) return -1; // qDebug() << "The CleavageRule name:" // << cleavage_rule_sp->getName() // << "Oligomer mono mass AFTER accounting it:" // << iter_oligomer_sp->getMass(Enums::MassType::MONO) // << "and internal formula has become:" // << iter_oligomer_sp->getFormulaCstRef().getActionFormula(); } // qDebug() << "Done iterating in the CleavageRule instances."; // At this point we can finally ionize the oligomer ! Remember // that we have to ionize the oligomer as expected in the // cleavage options. Because the ionization changes the values // in the oligomer, and we need a new oligomer each time, we // duplicate the oligomer each time we need it. int ionization_level = m_cleavageConfig.getStartIonizeLevel(); int ionization_stop_level = m_cleavageConfig.getStopIonizeLevel(); qDebug() << "The Ionizer:" << m_ionizer.toString(); // Sanity checks if(!m_ionizer.isValid()) qFatalStream() << "Programming error. The Ionizer cannot be invalid."; if(m_ionizer.isIonized()) qFatalStream() << "Programming error. The Ionizer cannot have ionized status."; // qDebug() << "BEFORE ionization:" // << "Oligomer mono mass:" // << iter_oligomer_sp->getMass(Enums::MassType::MONO) // << "and internal formula is:" // << iter_oligomer_sp->getFormulaCstRef().getActionFormula(); while(ionization_level <= ionization_stop_level) { Ionizer temp_ionizer(m_ionizer); temp_ionizer.setLevel(ionization_level); OligomerSPtr new_oligomer_sp = std::make_shared(*iter_oligomer_sp); new_oligomer_sp->setIonizer(temp_ionizer); if(new_oligomer_sp->ionize() == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to ionize the oligomer."; new_oligomer_sp.reset(); return -1; } bool ok = false; new_oligomer_sp->getFormulaRef().accountFormula( temp_ionizer.getFormulaCstRef().getActionFormula(), isotopic_data_csp, temp_ionizer.getLevel(), ok); if(!ok) { qWarning() << "Failed to account the ionizer formula:" << temp_ionizer.getFormulaCstRef().getActionFormula(); new_oligomer_sp.reset(); return -1; } // qDebug() << "AFTER ionization level:" << ionization_level // << "Oligomer mono mass:" // << iter_oligomer_sp->getMass(Enums::MassType::MONO) // << "and internal formula has become:" // << iter_oligomer_sp->getFormulaCstRef().getActionFormula(); // The name was set already during the creation of the // template oligomer. All we have to add to the name is the // ionization level. QString name = iter_oligomer_sp->getName() + QString("#z=%3").arg(temp_ionizer.charge()); new_oligomer_sp->setName(name); oligomer_buffer_container.getOligomersRef().push_back( new_oligomer_sp); ++ionization_level; } // qDebug() << "Going to erase partial oligomer:" // << (*partial_oligomers_iterator)->toString(); // We can now remove the template oligomer. partial_oligomers_iterator = partial_oligomers.getOligomersRef().erase(partial_oligomers_iterator); // No need to increment the iterator because we got new iterator from the // erase() call // above. Since we call erase() at each iteration, the iterator gets // updated at each iteration. } // qDebug() << "At this point, we had" << partial_oligomers_count // << "partial (uncharged) oligomers and we now have" // << oligomer_buffer_container.getOligomersCstRef().size() // << "buffer (charged) oligomers"; // Sanity check // There should be as many times more Oligomer in the buffer container // as there were charge levels to be performed, with respect to // the uncharged oligomers in the partial container. std::size_t buffer_oligomers_count = oligomer_buffer_container.getOligomersRef().size(); if(buffer_oligomers_count != (partial_oligomers_count * (m_cleavageConfig.getStopIonizeLevel() - m_cleavageConfig.getStartIonizeLevel() + 1))) qFatalStream() << "Programming error. The counts of Oligomer instances does not match."; // At this point we should transfer all the // oligomers from the oligomer_buffer_container to the initial // partial_oligomers. // Version involving much copying... // for(const OligomerSPtr &iter_oligomer_sp : // oligomer_buffer_container.getOligomersRef()) // partial_oligomers.getOligomersRef().push_back(iter_oligomer_sp); // oligomer_buffer_container.clear(); // Version involving a std::move operation. // std::size_t transferred_count = transferOligomers(oligomer_buffer_container, partial_oligomers); // qDebug() << "The number of transferred Oligomers:" << transferred_count; // qDebug() << "Count of oligomers in the source container:" // << oligomer_buffer_container.getOligomersCstRef().size(); // Sanity check if(oligomer_buffer_container.size()) qFatalStream() << "Programming error. The container cannot contain Oligomers anymore."; std::size_t generated_oligomers_count = partial_oligomers.size(); // Finally transfer all the oligomers generated for this partial // cleavage to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Old version involving much copying... // while(partial_oligomers.size()) // { // // Crucial to make this pointer cast so that we transfer actual // // Oligomers! // // OligomerSPtr iter_oligomer_sp = // std::dynamic_pointer_cast( // partial_oligomers.takeFirst()); // // //// Elemental formula // // QString *text = new // QString(iter_oligomer_sp->elementalComposition()); // // StringProp *prop = // // new StringProp("ELEMENTAL_COMPOSITION", text); // // iter_oligomer_sp->appendProp(static_cast(prop)); // // mp_oligomerList->append(iter_oligomer_sp); // } // New version still involving much copying... // for(const OligomerSPtr &iter_oligomer_sp : // partial_oligomers.getOligomersRef()) // m_oligomers.getOligomersRef().push_back(iter_oligomer_sp); // partial_oligomers.clear(); // Version involving a std::move operation. // size_t transferred_count = transferOligomers(partial_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" << transferred_count; // Sanity check if(partial_oligomers.size()) qFatalStream() << "Programming error. The container cannot contain Oligomers anymore."; return generated_oligomers_count; } /*! \brief Analyzes the CrossLink instances that might be involved in the Oligomer instances in the \a oligomers collection. The Oligomers that are found cross-linked are set in \a oligomers and the size of this collection is returned. */ int Cleaver::analyzeCrossLinks(OligomerCollection &oligomers) { OligomerCollection cross_linked_oligomers; // General overview: // At the moment this function is called only with oligomers that // were obtained with a full cleavage (no partial cleavages). // Thus, any given Monomer of the Polymer sequence is necessarily // contained only one Oligomer (see below). // Iterate in the polymer's list of cross-links and for each // cross-link find the one oligomer (because no partial cleavages) that // contains the first monomer involved in the cross-link. This first found // oligomer should serve as a bait to pull-down all the oligomers cross-linked // to it. const std::vector &polymer_cross_links = mcsp_polymer->getCrossLinksCstRef(); std::vector::const_iterator cross_link_iterator_cst = polymer_cross_links.cbegin(); std::vector::const_iterator cross_link_end_iterator_cst = polymer_cross_links.cend(); while(cross_link_iterator_cst != cross_link_end_iterator_cst) { // Get the first monomer that is involved in the CrossLink. CrossLinkCstSPtr cross_link_sp = *cross_link_iterator_cst; MonomerCstSPtr first_cross_linked_monomer_csp = cross_link_sp->getFirstMonomer(); if(first_cross_linked_monomer_csp == nullptr) qFatalStream() << "Programming error. Cannot be that the CrossLink has no " "Monomer in its container."; // In the whole set of Oligomer instances passed as argument, find the ONE // oligomer that encompasses that first Monomer involved in the CrossLink // currently iterated into. That is, the question is: "what is the // Oligomer that happens to contain that Monomer that is involved in the // CrossLink ? ". std::size_t oligomer_index_that_encompasses_monomer = 0; OligomerSPtr first_cross_linked_oligomer_sp = oligomers.findOligomerEncompassing( first_cross_linked_monomer_csp, oligomer_index_that_encompasses_monomer); if(first_cross_linked_oligomer_sp != nullptr) { // At this point we should turn this oligomer into a // cross-linked oligomer, so that we can continue performing its // cross-link analysis. To do that we allocate a list of // oligomers for this cross-linked oligomer, were we'll store // this first oligomer and then all the "pulled-down" oligomers. // Set the cross-linked oligomer apart. cross_linked_oligomers.getOligomersRef().push_back( first_cross_linked_oligomer_sp); // Remove the cross-link from the main list of oligomers so // that we do not stumble upon it in the next analysis // steps. oligomers.getOligomersRef().erase( oligomers.getOligomersRef().begin() + oligomer_index_that_encompasses_monomer); // Finally, deeply scrutinize that oligomer that is used as a bait // to pull down all the Oligomers that are cross-linked to it. analyzeCrossLinkedOligomer(first_cross_linked_oligomer_sp, oligomers); } else { // qDebug() << __FILE__ << __LINE__ // << "Cross-link at index" << iter // << "did not find any oligomer for its first monomer " // "partner"; } ++cross_link_iterator_cst; } // At this point we have terminated analyzing all the oligomers // for the partial cleavage. All we have to do is move all the // crossLinked oligomers from the cross_linked_oligomers to // oligomers. While doing so make sure that the m_calcOptions // datum has correct IndexRangeCollection data, as these data will be // required later, typically to calculate the elemental formula of // the oligomer. // Old version involving much copying. // while(cross_linked_oligomers.size()) // { // OligomerSPtr oligomer_sp = cross_linked_oligomers.takeAt(0); // oligomer_sp->updateCalcOptions(); // oligomers->append(oligomer_sp); // } // cross_linked_oligomers.clear(); // // New more C++ modern version. for(OligomerSPtr &oligomer_sp : cross_linked_oligomers.getOligomersRef()) { oligomers.getOligomersRef().push_back(std::move(oligomer_sp)); } // Sanity check: for(OligomerSPtr &oligomer_sp : cross_linked_oligomers.getOligomersRef()) { if(oligomer_sp != nullptr) qFatalStream() << "The oligomer was not moved."; } cross_linked_oligomers.clear(); // Return the number of cross-linked/non-cross-linked oligomers // alltogether. return oligomers.size(); } /*! \brief Pulls down all the Oligomer instances in \a oligomers that are found to be cross-linked to \a oligomer_sp. Returns the count of IndexRange instances found in \a oligomer_sp, which is a reflection of the number of oligomers that are found to be cross-linked to it. */ int Cleaver::analyzeCrossLinkedOligomer(OligomerSPtr oligomer_sp, OligomerCollection &oligomers) { // We get a cross-linked oligomer, previously found to contain the first // Monomer involved in a Polymer CrossLink. We want to // use that oligomer_sp as a bait to pull down all the other // oligomers that are cross-linked to it. // For that, we iterate in the oligomer_sp's Monomer // instances one by one and for each Monomer we ask the // mcsp_polymer to fill-in a container of CrossLink indices // in that Polymer that involve the Monomer being iterated into. // For each CrossLink at the indices reported above: // 1. its shared pointer is added to the oligomer_sp container of // cross-links. // 2. for each Monomer involved in the CrossLink, the list of // oligomers that is passed as argument is asked to return // an Oligomer that encompasses that Monomer. // The Oligomer returned at point 2 above (found_oligomer_sp) // (if non-nullptr) is pushed back to // a container of Oligomer instances (clearance_oligomers). Then that returned // Oligomer is removed from the initial container of Oligomers passed as // argument to this function (oligomers). // Finally, the original Oligomer (oligomer_sp) has its name chanaged to // indicate the cross-link between itself the the found oligomer. // At this point we have one oligomer which we know is cross-linked // at least once (with another oligomer or the cross-link is between // two or more monomers in the same oligomer, think cyan fluorescent // protein). If monomers in that same oligomer were cross-linked to // other monomers in other oligomers, then these oligomers should by // now have been moved from the original list of oligomers // (oligomers_sp) to the clearance list of oligomers // (clearance_oligomers). We have to iterate in each oligomer of that // clearance list and for each of its monomers, check if it has a // cross-link to any oligomer still in the original oligomers // (this is what I call "pull-down" stuff). Found oligomers are // appended to the clearance_oligomers. if(oligomer_sp == nullptr || oligomer_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; OligomerCollection clearance_oligomers; // 'oligomer_sp' is the first oligomer in the cross-link series of // oligomers. It is the "seeding" oligomer with which to pull-down // all the others. Prepend to its name the "cl-" string to let it // know it is cross-linked. QString name = oligomer_sp->getName(); name.prepend("cl-"); oligomer_sp->setName(name); // Iterate in the 'oligomer_sp' and for each monomer get any // cross-linked oligomer out of the list of cross-links. bool ok = false; std::size_t index_start = oligomer_sp->startIndex(ok); if(!ok) return -1; std::size_t index_stop = oligomer_sp->stopIndex(ok); if(!ok) return -1; // qDebug() << "Oligomer start:" << index_start << "stop:" << index_stop; const std::vector &polymer_cross_links = mcsp_polymer->getCrossLinksCstRef(); for(std::size_t iter = index_start; iter < index_stop + 1; ++iter) { // qDebug() << "iter:" << iter; MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // What crossLinks do involve this monomer ? std::vector cross_link_indices = mcsp_polymer->crossLinkIndicesInvolvingMonomer(monomer_csp.get()); // At least one cross-link involves the monomer currently // iterated into inside the oligomer being analysed. for(const std::size_t &index : cross_link_indices) { CrossLinkSPtr cross_link_sp = polymer_cross_links.at(index); if(cross_link_sp == nullptr) qFatalStream() << "Programming error."; // qDebug() << __FILE__ << __LINE__ // << cross_link_sp->getName(); // First off, we can add the cross-link to the list of // cross-links of the oligomer (we'll need them to be // able to perform mass calculations). Note that this is // only copying the pointer to the actual cross-link in // the polymer's list of cross-links. Note also that a // cross-link might not be found more than once(the // call below first checks that the cross-link is not // already in the list). if(!oligomer_sp->addCrossLink(cross_link_sp)) { // qDebug() << "The cross-link:" // << cross_link_sp->getName() // << "was already in the" // << oligomer // << "oligomer's list of cross-links: " // "not duplicated."; } else { // qDebug() << "The cross-link:" // << cross_link_sp->getName() // << "was added to the" // << oligomer // << "oligomer's list of cross-links."; } for(const MonomerCstSPtr &monomer_csp : cross_link_sp->getMonomersCstRef()) { // qDebug() << monomer_csp->getName(); std::size_t found_index = 0; OligomerSPtr found_oligomer_sp = oligomers.findOligomerEncompassing(monomer_csp, found_index); if(found_oligomer_sp != nullptr) { // qDebug() << found_oligomer_sp->getName() << // found_index; // One oligomer in the original oligomer list // encompasses a monomer that seems to be // cross-linked to the 'monomer' being iterated // in in the currently analyzed oligomer. Move // that oligomer to the clearance list of // oligomer that will need to be further // analyzed later. // Old version // oligomers.removeAt(found_index); clearance_oligomers.getOligomersRef().push_back( found_oligomer_sp); oligomers.getOligomersRef().erase( oligomers.getOligomersRef().begin() + found_index); // Update the name of the oligomer with the name // of the new found_oligomer_sp. QString name = QString("%1+%2") .arg(oligomer_sp->getName()) .arg(found_oligomer_sp->getName()); oligomer_sp->setName(name); } } } // End of // for(const std::size_t &index : cross_link_indices) } // End of // for(std::size_t iter = index_start; iter < index_stop + 1; ++iter) // At this point we have one oligomer which we know is cross-linked // at least once (with another oligomer or the cross-link is between // two or more monomers in the same oligomer, think cyan fluorescent // protein). If monomers in that same oligomer were cross-linked to // other monomers in other oligomers, then these oligomers should by // now have been moved from the original list of oligomers // (oligomers_sp) to the clearance list of oligomers // (clearance_oligomers). We have to iterate in each oligomer of that // clearance list and for each of its monomers, check if it has a // cross-link to any oligomer still in the original oligomers // (this is what I call "pull-down" stuff). Found oligomers are // appended to the clearance_oligomers. while(clearance_oligomers.size()) { OligomerSPtr iter_oligomer_sp = clearance_oligomers.getOligomersRef().front(); bool ok = false; std::size_t index_start = iter_oligomer_sp->startIndex(ok); if(!ok) return -1; std::size_t index_stop = iter_oligomer_sp->stopIndex(ok); if(!ok) return -1; for(std::size_t iter = index_start; iter <= index_stop; ++iter) { MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // qDebug() << __FILE__ << __LINE__ // << monomer_sp->getName(); // What crossLinks do involve this monomer ? std::vector cross_link_indices = mcsp_polymer->crossLinkIndicesInvolvingMonomer(monomer_csp.get()); if(cross_link_indices.size()) { // At least one cross-link involves the monomer currently // iterated in the iter_oligomer_sp being analysed. for(const std::size_t &index : cross_link_indices) { CrossLinkSPtr cross_link_sp = polymer_cross_links.at(index); if(cross_link_sp == nullptr) qFatalStream() << "Programming error."; // qDebug() << __FILE__ << __LINE__ // << cross_link_sp->getName(); // First off, we can add the cross-link to the list of // cross-links of the oligomer(we'll need them to be // able to perform mass calculations). Note that this is // only copying the pointer to the actual cross-link in // the polymer's list of cross-links. Note also that a // cross-link might not be found more than once(the // call below first checks that the cross-link is not // already in the list). if(!oligomer_sp->addCrossLink(cross_link_sp)) { // qDebug() << __FILE__ << __LINE__ // << "The cross-link:" // << cross_link_sp->getName() // << "was already in the" // << oligomer // << "oligomer's list of cross-links: " // "not duplicated."; } else { // qDebug() << __FILE__ << __LINE__ // << "The cross-link:" // << cross_link_sp->getName() // << "was added to the" // << oligomer // << "oligomer's list of cross-links."; } for(const MonomerCstSPtr &monomer_csp : cross_link_sp->getMonomersCstRef()) { // qDebug() << monomer_csp->getName(); std::size_t found_index = 0; OligomerSPtr found_oligomer_sp = oligomers.findOligomerEncompassing(monomer_csp, found_index); if(found_oligomer_sp) { // qDebug() << __FILE__ << __LINE__ // << foundOligomer->name() << foundIndex; // One oligomer in the original oligomer list // encompasses a monomer that seems to be // cross-linked to the 'monomer' being iterated // in in the currently analyzed oligomer. Move // that oligomer to the clearance list of // oligomer that will need to be further // analyzed later. // Old version // oligomers.removeAt(foundIndex); clearance_oligomers.getOligomersRef().push_back( found_oligomer_sp); oligomers.getOligomersRef().erase( oligomers.getOligomersRef().begin() + found_index); // Update the name of the oligomer with the name // of the new found_oligomer_sp. QString name = QString("%1+%2") .arg(oligomer_sp->getName()) .arg(found_oligomer_sp->getName()); oligomer_sp->setName(name); } } } // End of // foreach(index, cross_link_indices) } // End of(ret) ie cross-links involved monomer } // End of // for (int iter = iter_oligomer_sp->index_start(); // iter < iter_oligomer_sp->index_stop() + 1; ++iter) // At this point this quarantinized oligomer might be removed // from the clearance_oligomers and its sequence_range // be appended to the 'oligomer' list of sequence_range. Then, the // quanrantinized oligomer might be destroyed. oligomer_sp->getIndexRangeCollectionRef().appendIndexRanges( iter_oligomer_sp->getIndexRangeCollectionRef()); // That oligomer was gotten with front(), so it is the // first Oligomer in the vector. The corresponding iterator // is thus begin(). clearance_oligomers.getOligomersRef().erase( clearance_oligomers.getOligomersRef().begin()); } // At this point, all the oligomers in the clearance oligomer list // have all been dealt with, return the number of cross-linked // oligomers in this oligomer. return oligomer_sp->getIndexRangeCollectionRef().size(); } //////////////// OPERATORS ///////////////////// /*! \brief Returns a reference to this Cleaver instance after initialization using \a other. */ Cleaver & Cleaver::operator=(const Cleaver &other) { if(this == &other) return *this; mcsp_polymer = other.mcsp_polymer; mcsp_polChemDef = other.mcsp_polChemDef; m_cleavageConfig.initialize(other.m_cleavageConfig); m_calcOptions.initialize(other.m_calcOptions); m_ionizer = other.m_ionizer; m_doCleaveIndices.assign(other.m_doCleaveIndices.begin(), other.m_doCleaveIndices.end()); m_doNotCleaveIndices.assign(other.m_doNotCleaveIndices.begin(), other.m_doNotCleaveIndices.end()); m_oligomers = other.m_oligomers; return *this; } /*! \brief Returns true if this instance is identical to \a other, false otherwise. */ bool Cleaver::operator==(const Cleaver &other) const { if(this == &other) return true; if(mcsp_polymer != other.mcsp_polymer || mcsp_polChemDef != other.mcsp_polChemDef || m_cleavageConfig != other.m_cleavageConfig || m_calcOptions != other.m_calcOptions || m_ionizer != other.m_ionizer || m_oligomers != other.m_oligomers) return false; return true; } /*! \brief Returns true if this instance is different than \a other, false otherwise. */ bool Cleaver::operator!=(const Cleaver &other) const { return !operator==(other); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CrossLink.cpp000664 001750 001750 00000124114 15100504560 024701 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CrossLink.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" int crossLinkMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::CrossLink"); int crossLinkPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::CrossLinkPtr"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CrossLink \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CrossLink.hpp \brief The CrossLink class provides abstractions to work with a cross-link entity between \l Monomer instances. The notion of a cross-link is that it is a chemical reaction that involves at least two monomers in a \l Polymer or in an \l Oligomer sequence. Polymer sequences might contain more than one CrossLink and these are stored in a container. */ /*! \variable MsXpS::libXpertMassCore::CrossLink::mcsp_crossLinker \brief The \l CrossLinker instance defining the chemistry of this CrossLink. */ /*! \variable MsXpS::libXpertMassCore::CrossLink::mcsp_polymer \brief The \l Polymer instance of which this CrossLink is part. */ /*! \variable MsXpS::libXpertMassCore::CrossLink::m_monomers \brief The container of \l Monomer instances that are involved in the formation of this CrossLink. \note The Monomer pointers stored in the m_monomers member are MonomerCstSPtr that point to MonomerSPtr items stored in the \l{Polymer}'s \l{Sequence}'s vector of of MonomerSPtr (authorized implicit to-const cast). Because it is not possible to perform the to-non-const back-cast, when needed to access the \l{Sequence}'s MonomerSPtr, the search is performed by providing the Monomer raw pointer managed by the MonomerSPtr in m_monomers. The reason why the CrossLink stores the involved \l Monomer instances as pointers to these very Monomer instances in the polymer sequence is that in this way, even if the sequence is edited, the cross-link does not loose the relation to the monomers. The only way that the sequence editing modifies a CrossLink instance is by removing at least one \l Monomer instance involved in the CrossLink. If that occurs, the CrossLink is informed and it is destructed. */ /*! \variable MsXpS::libXpertMassCore::CrossLink::m_comment \brief The comment that might be associated to this CrossLink. */ /*! \typedef MsXpS::libXpertMassCore::CrossLinkSPtr \relates MsXpS::libXpertMassCore::CrossLink Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::CrossLinkCstSPtr \relates MsXpS::libXpertMassCore::CrossLink Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::CrossLinkWPtr \relates MsXpS::libXpertMassCore::CrossLink Synonym for std::weak_ptr. */ /*! \typedef MsXpS::libXpertMassCore::CrossLinkCstWPtr \relates MsXpS::libXpertMassCore::CrossLink Synonym for std::weak_ptr. */ /*! \typealias MsXpS::libXpertMassCore::UuidCrossLinkCstWPtrPair \relates MsXpS::libXpertMassCore::CrossLink Synonym for std::pair. */ /*! \typealias MsXpS::libXpertMassCore::UuidCrossLinkWPtrPair \relates MsXpS::libXpertMassCore::CrossLink \brief Synonym for std::pair. These pairs are used to store a unique identifier (Uuid) string related to a std::shared_ptr type. This kind of pair is used in a container in the \l Polymer class. The fact that the std::shared_ptr is converted to a std::weak_ptr is interesting because the item in the pair will not increase the reference count. */ /*! \brief Constructs a totally empty CrossLink instance */ CrossLink::CrossLink() { } /*! \brief Constructs a CrossLink instance \list \li \a pol_chem_def_csp: the polymer chemistry definition (\l PolChemDef). \li \a polymer_cqsp: the \l Polymer instance in which this CrossLink was formed. \li \a name: the name of this CrossLink instance. \li \a formula: the \l Formula that describes the reaction that is the basis of the chemical reaction leading to the formation of this CrossLink. \li \a comment: a comment that might be associated to this CrossLink. \endlist \note Providing a nullptr for \a polymer_cqsp is fatal. */ CrossLink::CrossLink(PolChemDefCstSPtr pol_chem_def_csp, PolymerCstQSPtr polymer_cqsp, const QString &name, const QString &formula, const QString &comment) : mcsp_crossLinker( std::make_shared(pol_chem_def_csp, name, formula)), mcsp_polymer(polymer_cqsp), m_comment(comment) { if(polymer_cqsp == nullptr || polymer_cqsp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr. If that pointer " "was gotten from Polymer::getCstSharedPtr(), ensure that raw pointer " "is " "indeed managed by a std::shared_ptr< Polymer> shared pointer."; } /*! \brief Constructs a CrossLink instance \list \li \a cross_linker_csp: CrossLinker instance used to initialize this CrossLink. \li \a polymer_cqsp: the \l Polymer instance in which this CrossLink was formed \li \a comment: a comment that might be associated to this CrossLink \endlist \note Providing a nullptr for \a polymer_cqsp is fatal. */ CrossLink::CrossLink(CrossLinkerCstSPtr cross_linker_csp, PolymerCstQSPtr polymer_cqsp, const QString &comment) : mcsp_crossLinker(cross_linker_csp), mcsp_polymer(polymer_cqsp), m_comment(comment) { if(polymer_cqsp == nullptr || polymer_cqsp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr. If that pointer " "was gotten from Polymer::getCstSharedPtr(), ensure that raw pointer " "is " "indeed managed by a std::shared_ptr< Polymer> shared pointer."; } /*! \brief Constructs a CrossLink instance as a copy of \a other. */ CrossLink::CrossLink(const CrossLink &other) : mcsp_crossLinker(other.mcsp_crossLinker), mcsp_polymer(other.mcsp_polymer), m_comment(other.m_comment), m_monomers(other.m_monomers) { } /*! \brief Destructs this CrossLink instance. No entity needs to be destructed, since the \l Monomer instances in the list are not owned by this CrossLinker instance. */ CrossLink::~CrossLink() { } //////////////// THE POLYMER ///////////////////// /*! \brief Returns the \l Polymer instance in which this CrossLink has been formed. */ PolymerCstQSPtr CrossLink::getPolymerCstSPtr() const { return mcsp_polymer; } //////////////// THE CROSS-LINKER ///////////////////// /*! \brief Returns the member CrossLinker. */ const CrossLinkerCstSPtr CrossLink::getCrossLinkerCstSPtr() const { return mcsp_crossLinker; } //////////////// THE COMMENT ///////////////////// /*! \brief Sets the \a comment. */ void CrossLink::setComment(const QString &comment) { m_comment = comment; } /*! \brief Returns the comment. */ const QString & CrossLink::getComment() const { return m_comment; } //////////////// THE NAME ///////////////////// /*! \brief Returns the name of the member \l CrossLinker. */ QString CrossLink::getCrossLinkerName() const { if(mcsp_crossLinker != nullptr) return mcsp_crossLinker->getName(); return QString(); } //////////////// THE MONOMERS ///////////////////// /*! \brief Returns a const reference to the Monomer container. */ const std::vector & CrossLink::getMonomersCstRef() const { return m_monomers; } /*! \brief Returns a reference to the Monomer container. */ std::vector & CrossLink::getMonomersRef() { return m_monomers; } /*! \brief Adds \a monomer_csp to the member container of Monomer instances and returns the matching QUuid value string. */ QString CrossLink::appendMonomer(MonomerCstSPtr monomer_csp) { return storeMonomer(monomer_csp); } /*! \brief Returns the Monomer at \a index in the member container of \l Monomer instances. \a index cannot be out of bounds. */ MonomerCstSPtr CrossLink::getMonomerAt(std::size_t index) { if(index >= m_monomers.size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_monomers.at(index); } /*! \brief Removes the Monomer at \a index in the member container of \l Monomer instances. \a index cannot be out of bounds. Returns true if a Monomer was indeed removed, false otherwise. */ bool CrossLink::removeMonomerAt(std::size_t index) { if(index >= m_monomers.size()) qFatalStream() << "Programming error. Index is out of bounds."; MonomerCstSPtr monomer_csp = *(m_monomers.cbegin() + index); if(monomer_csp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; return removeMonomer(monomer_csp); } /*! \brief Removes Monomer \a monomer_csp from the member container of Monomer intances. Returns true if a Monomer was indeed removed, false otherwise. */ bool CrossLink::removeMonomer(MonomerCstSPtr monomer_csp) { if(monomer_csp == nullptr || monomer_csp.get() == nullptr) qFatalStream() << "Cannot be that pointer is nullptr."; // We will need this anyway. QString uuid = getUuidForMonomer(monomer_csp); std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.begin(), m_monomers.end(), [monomer_csp](MonomerCstSPtr iter_modif_sp) { return iter_modif_sp == monomer_csp; }); if(the_iterator_cst == m_monomers.end()) { qCritical() << "The MonomerCstSPtr was not found in the container."; if(!uuid.isEmpty()) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; return false; } // At this point, both containers contain modif_sp. m_monomers.erase(the_iterator_cst); std::vector::const_iterator the_pair_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [uuid](const UuidMonomerCstWPtrPair &the_pair) { // Do not query the modif_sp managed object because it can // be nullptr! return the_pair.first == uuid; }); if(the_pair_iterator_cst == m_uuidMonomerPairs.cend()) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; m_uuidMonomerPairs.erase(the_pair_iterator_cst); return true; } /*! \brief Sets in the Monomer container, the Monomer instances occurring at the indices listed in \a monomer_indices_text. \a monomer_indices_text contains a list of \l Monomer instance indices separated by ';' characters. The corresponding monomers (MonomerSPtr) found in the member Polymer's Sequence object are copied to the member list of \l Monomer instances as MonomerCstSPtr using the UuidMonomerCstWPtrPair-based logic. This process effectively documents a CrossLink in that member Polymer's Sequence. If the process is successful, \a ok is set to true, otherwise it is set to false. Returns the new size of the member container of Monomer instances. */ std::size_t CrossLink::fillInMonomers(QString monomer_indices_text, bool &ok) { m_monomers.clear(); QStringList indices_list = monomer_indices_text.split(';', Qt::SkipEmptyParts); // There must be at least 2 monomers to make a cross-link ! if(indices_list.size() < 2) { qCritical() << "The count of indices must be greater than 2."; ok = false; return 0; } for(auto &index_string : indices_list) { bool res = false; std::size_t index = index_string.toInt(&res); if(!res) { ok = false; return 0; } if(index >= mcsp_polymer->size()) { ok = false; m_monomers.clear(); return 0; } storeMonomer( mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index)); } ok = true; return m_monomers.size(); } /*! \brief Returns a container with all the locations of the Monomer instances involved in this CrossLink. If \a location_type is Enums::LocationType::INDEX, the locations are numbered as indices. If \a location_type is Enums::LocationType::POSITION, the locations are numbered as positions (that is, index + 1). For example, if the cross-link involves Monomer instances at indices 20, 45, 89, then the indices are this continuum of indices: {20;21;22;...;87;88;89}. */ std::vector CrossLink::continuumOfLocationsOfInclusiveSequenceMonomers( Enums::LocationType location_type) const { // First get indices. std::vector indices; // qDebug() << "There are" << m_monomers.size() // << "Monomer pointers in this cross-link."; for(const MonomerCstSPtr &monomer_csp : m_monomers) { bool ok = false; std::size_t monomer_index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok); if(ok) indices.push_back(monomer_index); } // Sort the indices. std::sort(indices.begin(), indices.end()); // Now that we have the minIndex and the maxIndex of the region // involved by the cross-link, we can fill-in the integer list // with all the values contained between these two borders. std::size_t begin_index = (*indices.cbegin()); std::size_t end_index = (*std::prev(indices.cend())); // Only if positions are required, do we increment the indices by one. if(location_type == Enums::LocationType::POSITION) { ++begin_index; ++end_index; } std::vector locations; for(std::size_t iter = begin_index; iter <= end_index; ++iter) { // If we had a cross-link between monomers [4] and [10] of the // polymer, then the indices appended to the list would be 4, // 5, 6, 7, 8, 9 and 10. locations.push_back(iter); } return locations; } /*! \brief Returns a string containing a ';'-separated list of the locations of all the \l Monomer instances involved in this CrossLink. If \a location_type is Enums::LocationType::INDEX, indices are formatted. If \a location_type is Enums::LocationType::POSITION, positions are formatted (that is, indices + 1). */ QString CrossLink::continuumOfLocationsOfInclusiveSequenceMonomersAsText( Enums::LocationType location_type) const { std::vector locations = continuumOfLocationsOfInclusiveSequenceMonomers(location_type); return formatContainerOfMonomerLocationsAsText(locations); } /*! \brief Returns a container with the locations of the two extreme Monomer instances involved in this CrossLink. If \a location_type is Enums::LocationType::INDEX, the locations are numbered as indices. If \a location_type is Enums::LocationType::POSITION, the locations are numbered as positions (that is, index + 1). For example, if the cross-link involves Monomer instances at indices 20, 45, 89, then the indices are this range of indices: [20--89]. */ std::vector CrossLink::locationsOfOnlyExtremeSequenceMonomers( Enums::LocationType location_type) const { std::vector continuum_locations = continuumOfLocationsOfInclusiveSequenceMonomers(location_type); if(continuum_locations.size() < 2) qFatalStream() << "Programming error. Cannot be that less than two monomers are " "involved in CrossLink."; std::vector extreme_locations; // Only keep first and last. extreme_locations.push_back(continuum_locations.front()); extreme_locations.push_back(continuum_locations.back()); return extreme_locations; } /*! \brief Returns a string containing a ';'-separated list of the locations of the two extreme \l Monomer instances involved in this CrossLink. If \a location_type is Enums::LocationType::INDEX, indices are formatted. If \a location_type is Enums::LocationType::POSITION, positions are formatted (that is, indices + 1). */ QString CrossLink::locationsOfOnlyExtremeSequenceMonomersAsText( Enums::LocationType location_type) const { std::vector locations = locationsOfOnlyExtremeSequenceMonomers(location_type); return formatContainerOfMonomerLocationsAsText(locations); } /*! \brief Returns the index of \a monomer_csp as found in the member container of Monomer instances. \a monomer_csp is typically a Monomer found in the member Polymer for which this function establishes if it is involved in this CrossLink. If \a monomer_csp is not found, \a ok is set to false and 0 is returned, otherwise \a ok is set to true and the index of the found Monomer is returned. */ std::size_t CrossLink::monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const { if(monomer_csp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [&](const MonomerCstSPtr &iter_monomer_csp) { return iter_monomer_csp == monomer_csp; }); if(the_iterator_cst == m_monomers.cend()) { // Let the caller know. ok = false; return 0; } // Let the caller know. ok = true; return std::distance(m_monomers.cbegin(), the_iterator_cst); } /*! \brief Returns the index of \a monomer_crp as found in the container of Monomer instances. \a monomer_crp is typically a Monomer found in the member Polymer for which this function establishes if it is involved in this CrossLink. If \a monomer_crp is not found, \a ok is set to false and 0 is returned, otherwise \a ok is set to true and the index of the found Monomer is returned. */ std::size_t CrossLink::monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const { if(monomer_crp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [&](const MonomerCstSPtr &iter_monomer_csp) { return iter_monomer_csp.get() == monomer_crp; }); if(the_iterator_cst == m_monomers.cend()) { // Let the caller know. ok = false; return 0; } // Let the caller know. ok = true; return std::distance(m_monomers.cbegin(), the_iterator_cst); } /*! \brief Returns the first \l Monomer instance in the member container of monomers. If the container is empty; returns nullptr. */ MonomerCstSPtr CrossLink::getFirstMonomer() const { if(!m_monomers.size()) return nullptr; return m_monomers.front(); } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this CrossLink instance. After having set the member data, this CrossLink instance is validated and the result is set to m_isValid. Returns a reference to this CrossLink instance. */ CrossLink & CrossLink::operator=(const CrossLink &other) { if(&other == this) return *this; mcsp_crossLinker = other.mcsp_crossLinker; mcsp_polymer = other.mcsp_polymer; m_comment = other.m_comment; // The Monomer instances are deeply duplicated. for(const MonomerCstSPtr &monomer_csp : other.m_monomers) storeMonomer(monomer_csp); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The assignment operator copying produced a CrossLink that is not " "valid, with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if this CrossLink instance and \a other are identical, false otherwise. The monomers are compared deeply (non on the basis of the pointer value). */ bool CrossLink::operator==(const CrossLink &other) const { if(&other == this) return true; if(mcsp_crossLinker != other.mcsp_crossLinker || mcsp_polymer != other.mcsp_polymer || m_comment != other.m_comment) return false; // Because we are dealing with Monomer shared pointers to Monomer instances // found in the containing Polymer, we must compare the pointers, not the // Monomer chemical entities. if(other.m_monomers.size() != m_monomers.size()) return false; for(std::size_t iter = 0; iter < other.m_monomers.size(); ++iter) { if(*m_monomers.at(iter) != *other.m_monomers.at(iter)) { // qDebug() << "At least one Monomer instance differs in both // CrossLink instances."; return false; } } return true; } /*! \brief Returns true if this CrossLink instance and \a other are different, false otherwise. Returns the negated result of operator==(other); */ bool CrossLink::operator!=(const CrossLink &other) const { if(&other == this) return false; return !operator==(other); } //////////////// CROSS-LINK LOGIC ///////////////////// /*! \brief Tells if this CrossLink instance is encompassed (partially or fully) or not at all by the Polymer sequence region defined by the [\a start - \a end ] \l Monomer index range. In other words, this function tells if a given set of Monomers are involved in this CrossLink. The count of monomers involved in this cross-link that: \list \li Are contained in the range is stored in \a in_count. \li Are not contained in the range is stored in \a out_count. \endlist If all the monomers are listed as \a in_count, then this CrossLink is fully encompassed by the Polymer sequence region defined by the [\a start - \a end ] \l Monomer index range and Enums::CrossLinkEncompassed::FULLY is returned. If all the monomers are listed as \a out_count, then this CrossLink is not at all encompassed by the Polymer sequence region and Enums::CrossLinkEncompassed::NOT is returned. If both kinds of monomers were found, then Enums::CrossLinkEncompassed::PARTIALLY is returned. */ Enums::CrossLinkEncompassed CrossLink::isEncompassedByIndexRange(std::size_t start, std::size_t end, std::size_t &in_count, std::size_t &out_count) const { if(mcsp_polymer == nullptr) qFatalStream() << "Programming error. The Polymer pointer cannot be nullptr."; // Iterate in the list of monomers, and for each monomer check if // their index is contained in the stretch. in_count = 0; out_count = 0; std::size_t local_start = std::min(start, end); std::size_t local_end = std::max(start, end); for(const MonomerCstSPtr &monomer_csp : m_monomers) { bool ok = false; std::size_t monomer_index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok); if(ok) { if(monomer_index >= local_start && monomer_index <= local_end) ++in_count; else ++out_count; } } if((in_count + out_count) != m_monomers.size()) qFatalStream() << "The count of monomers in and out should sum to the size of " "the Monomer container."; if(out_count && in_count) return Enums::CrossLinkEncompassed::PARTIALLY; if(out_count) return Enums::CrossLinkEncompassed::NOT; if(in_count) return Enums::CrossLinkEncompassed::FULLY; return Enums::CrossLinkEncompassed::NOT; } /*! \brief Tells if this CrossLink instance is encompassed (partially or fully) or not at all by various \l IndexRange instances in the \a index_ranges \l IndexRangeCollection. In other words, this function tells if a given set of Monomers are involved in this CrossLink. For examle, when \a index_ranges contains a single index range that resolves to a single Monomer index in the Sequence, this function simply says if the CrossLink involves that monomer. The count of monomers involved in this cross-link that: \list \li Are contained in the regions defined in \a index_ranges is stored in \a in_count. \li Are not contained in the range is stored in \a out_count. \endlist If all the monomers are listed as \a in_count, then this CrossLink is fully encompassed by the Polymer sequence regions defined in \a index_ranges and Enums::CrossLinkEncompassed::FULLY is returned. If all the monomers are listed as \a out_count, then this CrossLink is not at all encompassed by the Polymer sequence region and Enums::CrossLinkEncompassed::NOT is returned. If both kinds of monomers were found, then Enums::CrossLinkEncompassed::PARTIALLY is returned. */ Enums::CrossLinkEncompassed CrossLink::isEncompassedByIndexRangeCollection( const IndexRangeCollection &index_ranges, std::size_t &in_count, std::size_t &out_count) const { if(mcsp_polymer == nullptr) qFatalStream() << "Programming error. The Polymer pointer cannot be nullptr."; // Iterate in the list of monomers involved in *this crossLink, // and for each monomer check if their index is contained in any // of the Coordinates [start--end] of the coordinateList passed as // parameter. in_count = 0; out_count = 0; for(const MonomerCstSPtr &monomer_csp : m_monomers) { bool was_counted_in = false; bool ok = false; qsizetype monomer_index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok); if(ok) { // Is the index encompassed by any of the SequenceRange instances of // the SequenceRanges ? foreach(const IndexRange *item, index_ranges.getRangesCstRef()) { if(monomer_index >= item->m_start && monomer_index <= item->m_stop) { ++in_count; was_counted_in = true; break; } } if(!was_counted_in) ++out_count; } } if((in_count + out_count) != m_monomers.size()) qFatalStream() << "The count of monomers in and out should sum to the size of " "the Monomer container."; if(out_count && in_count) return Enums::CrossLinkEncompassed::PARTIALLY; if(out_count) return Enums::CrossLinkEncompassed::NOT; if(in_count) return Enums::CrossLinkEncompassed::FULLY; return Enums::CrossLinkEncompassed::NOT; } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns true if this CrossLink instance validates successfully, false otherwise. The validation is successful if: \list \li The count of \l Monomer instances listed in the member list is > 1. \li The member \l Polymer instance exists. \li The count of \l Monomer instances listed in the member list is equal to the number of \l Modif instance in the \l CrossLinker base class. \li If the list of \l Modif instances in the \l CrossLinker base class is not empty, there must be a colinearity between the order in which these instances are listed and the order in which the \l Monomer instances are listed in the member list. The monomer instance must be a target of the modification. \endlist Errors are reported as messages in \a error_list_p. */ bool CrossLink::validate(ErrorList *error_list_p) const { Q_ASSERT(error_list_p != nullptr); // Set it as valid, then we negate as we validate. m_isValid = true; if(m_monomers.size() <= 1) { error_list_p->push_back( "A CrossLink involving on one Monomer is invalid"); qCritical() << "A CrossLink involving on one Monomer is invalid."; m_isValid = false; } if(mcsp_polymer == nullptr) { error_list_p->push_back("A CrossLink with no Polymer is invalid"); qCritical() << "A CrossLink with no Polymer is invalid."; m_isValid = false; } for(const MonomerCstSPtr &monomer_csp : m_monomers) { if(!monomer_csp->validate(error_list_p)) { error_list_p->push_back( "A CrossLink involving invalid Monomer instances is invalid"); qCritical() << "A CrossLink involving invalid Monomer instances is invalid."; m_isValid = false; } } if(mcsp_crossLinker == nullptr) { error_list_p->push_back("A CrossLink with no CrossLinker is invalid"); qCritical() << "A CrossLink with no CrossLinker is invalid."; m_isValid = false; return m_isValid; } else { if(!mcsp_crossLinker->validate(error_list_p)) { error_list_p->push_back( "A CrossLink with an invalid CrossLinker is invalid"); qCritical() << "A CrossLink with an invalid CrossLinker is invalid."; m_isValid = false; return m_isValid; } // The CrossLinker might have Modif instances in its container. These // Modif instances // need to be compatible with the monomers involved in this CrossLink, // that is, their targets specifications need to be compatible with the // Monomers involved in this CrossLink. const std::vector &cross_linker_modifs = mcsp_crossLinker->getModifsCstRef(); // Indeed, there is some logic here. Either the m_modifList in the // parent class CrossLinker contains no item, in which case // everything is fine, or it does contain items. In the latter case, // then, the number of items must match the number of monomers being // crosslinked, and then we get to know which modif is attributable // to which monomer, hence the possibility of a check. if(cross_linker_modifs.size()) { if(cross_linker_modifs.size() != m_monomers.size()) { error_list_p->push_back( "A CrossLink where the number of Modif " "does not match the number of " "Monomer is invalid"); qCritical() << "A CrossLink where the number of Modif " "does not match the number of " "Monomer is invalid."; m_isValid = false; return m_isValid; } // At this point, we can make the check for each modif/monomer: for(std::size_t iter = 0; iter < m_monomers.size(); ++iter) { MonomerCstSPtr monomer_csp = m_monomers.at(iter); if(!monomer_csp->isModifTarget(*cross_linker_modifs.at(iter))) { error_list_p->push_back( "A CrossLink where the Monomer is not a target of Modif is " "invalid"); qCritical() << "A CrossLink where the Monomer is not a " "target of Modif is invalid"; m_isValid = false; return m_isValid; } } } } return m_isValid; } /*! \brief Returns the validity status of the CrossLink. */ bool CrossLink::isValid() const { return m_isValid; } //////////////// MASS OPERATIONS ///////////////////// /*! \brief Calculates the mass of the CrossLink as the mass of the CrossLinker. Sets the calculated masses to \a mono and \a avg. If \a isotopic_data_csp, these reference data are used for the calculation, otherwise those in the PolChemDef referenced by the CrossLinker are used. Returns true if the calculation could proceed without error. */ bool CrossLink::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const { if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; return mcsp_crossLinker->calculateMasses(isotopic_data_csp, mono, avg); } /*! \brief Adds this CrossLinker's member masses to \a mono and \a avg, as compounded by the \a times factor. Returns a reference to this CrossLink. */ const CrossLink & CrossLink::accountMasses(double &mono, double &avg, int times) const { if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; mcsp_crossLinker->accountMasses(mono, avg, times); qDebug() << "Done accounting masses for this CrossLink with CrossLinker:" << mcsp_crossLinker->getName(); return *this; } /*! \brief Returns the CrossLinker's mass of the type defined by \a mass_type. */ double CrossLink::getMass(Enums::MassType mass_type) const { if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; return mcsp_crossLinker->getMass(mass_type); } ////////////////UTILS ///////////////////// /*! \brief Returns an allocated string describing this CrossLink instance. */ QString CrossLink::prepareResultsTxtString() { if(mcsp_crossLinker == nullptr || mcsp_crossLinker.get() == nullptr) qFatalStream() << "Programming error. The CrossLinker pointer cannot be nullptr."; if(mcsp_polymer == nullptr) qFatalStream() << "Programming error. The Polymer pointer cannot be nullptr."; QString text; text += QObject::tr( "\nCross-link:\n" "===============\n" "Cross-linker name: %1\n" "Cross-link comment: %2\n") .arg(mcsp_crossLinker->getName()) .arg(m_comment); int iter = 1; for(const MonomerCstSPtr &monomer_csp : m_monomers) { bool ok = false; std::size_t monomer_index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp.get(), ok); text += QObject::tr("Partner %1: %2 at position: %3\n") .arg(iter) .arg(monomer_csp->getCode()) .arg(monomer_index + 1); ++iter; } return text; } /*! \brief Crafts and returns a string in the form ";;;" corresponding to the values stored in the \a locations container. This function is used to format either Monomer indices or Monomer positions, which is why it has the neutral "locations" term in its name. If the container is empty, an empty string is returned. \sa continuumOfLocationsOfInclusiveSequenceMonomers(), locationsOfOnlyExtremeSequenceMonomers(), locationsOfOnlyExtremeSequenceMonomersAsText() */ QString CrossLink::formatContainerOfMonomerLocationsAsText( const std::vector &locations) const { if(!locations.size()) return QString(); QString text = ";"; for(std::size_t location : locations) text += QString("%1;").arg(location); return text; } /*! \brief Stores the Monomer instance \a monomer_csp pointer in the member container. The \a monomer_csp is stored as is, without duplication. Returns the Uuid string associated to the stored Monomer. */ QString CrossLink::storeMonomer(const MonomerCstSPtr &monomer_csp) { if(monomer_csp == nullptr) qFatalStream() << "Cannot be that the pointer is nullptr."; qDebug() << "Right before storage, there are currently" << m_monomers.size() << "monomers."; // Do not store an item twice. if(hasMonomer(monomer_csp) || !getUuidForMonomer(monomer_csp).isEmpty()) qFatalStream() << "It is prohibited to store the same MonomerCstSPtr more than once."; // Even if we get a ref to shared_ptr, the reference count increment will // occur. m_monomers.push_back(monomer_csp); QString uuid = QUuid::createUuid().toString(); m_uuidMonomerPairs.push_back(UuidMonomerCstWPtrPair(uuid, monomer_csp)); qDebug() << "Right after storage, there are currently" << m_monomers.size() << "monomers."; return uuid; } /*! \brief Returns true if \a monomer_csp was found in the member container of Monomer instances, false otherwise. */ bool CrossLink::hasMonomer(const MonomerCstSPtr &monomer_csp) const { if(monomer_csp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [monomer_csp](const MonomerCstSPtr &the_monomer_csp) { return the_monomer_csp == monomer_csp; }); if(the_iterator_cst == m_monomers.cend()) return false; // No sanity checks with getMonomerFromUuid() or getUuidForMonomer() // because that makes circular calls (these functions make sanity // checks by calling this hasMonomer().) return true; } /*! \brief Returns true if \a monomer_csp was found in the member container of Uuid-Monomer pairs, false otherwise. */ bool CrossLink::hasUuid(const MonomerCstSPtr &monomer_csp) const { if(monomer_csp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [monomer_csp](const UuidMonomerCstWPtrPair &the_pair) { return the_pair.second.lock() == monomer_csp; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) return false; // Sanity check if(!hasMonomer(monomer_csp)) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; return true; } /*! \brief Returns the Monomer instance pointer in the member container that is associated to the \a uuid Uuid string. If no such Monomer instance pointer is found, nullptr is returned. */ MonomerCstSPtr CrossLink::getMonomerForUuid(const QString &uuid) const { qDebug() << "There are currently" << m_monomers.size() << "monomers. The uuid that is asked for:" << uuid; std::vector>::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [uuid](const UuidMonomerCstWPtrPair &the_pair) { return the_pair.first == uuid; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) return nullptr; MonomerCstSPtr monomer_csp = (*the_iterator_cst).second.lock(); // Sanity check if(!hasMonomer(monomer_csp)) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; return monomer_csp; } /*! \brief Returns the UUID string identifying \a monomer_csp in the member container. If no such Monomer is found, an empty string is returned. */ QString CrossLink::getUuidForMonomer(const MonomerCstSPtr &monomer_csp) const { if(monomer_csp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [monomer_csp](const UuidMonomerCstWPtrPair &the_pair) { // Do not query the monomer_sp managed object because it can // be nullptr! return the_pair.second.lock() == monomer_csp; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) { // Sanity check if(hasMonomer(monomer_csp)) qFatalStream() << "Inconsistency between the m_monomers and the " "m_uuidMonomerPairs vectors."; return QString(); } // Sanity check if(!hasMonomer(monomer_csp)) qFatalStream() << "Inconsistency between the m_monomers and the " "m_uuidMonomerPairs vectors."; return (*the_iterator_cst).first; } /*! \brief Returns a container of QString instances that correspond to the UUID strings that identify all the Monomer instances involved in this CrossLink. If no Monomer is found, an empty container is returned. */ std::vector CrossLink::getAllMonomerUuids() const { std::vector the_uuid_strings; for(const UuidMonomerCstWPtrPair &pair : m_uuidMonomerPairs) the_uuid_strings.push_back(pair.first); // Sanity check if(the_uuid_strings.size() != m_monomers.size()) qFatalStream() << "Inconsistency between the _s and pairs."; return the_uuid_strings; } /*! \brief Returns a text string describing this CrossLink instance. */ QString CrossLink::toString() const { QString text = QString("CrossLink object of CrossLinker: %1 - ").arg(getCrossLinkerName()); text += QString("with %1 involved monomers at indices [").arg(m_monomers.size()); bool ok = false; for(MonomerCstSPtr monomer_csp : m_monomers) { std::size_t index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_csp, ok); if(!ok) qFatalStream() << "Programming error."; text += QString(";%1;").arg(index); } text += "]"; return text; } /*! \brief Removes from the member container all the Monomer instance pointers that are not found to still be alive. */ void CrossLink::cleanupMonomers() { qDebug() << "At beginning, count of UUID-Monomer pairs:" << m_uuidMonomerPairs.size(); std::vector::iterator the_iterator = m_uuidMonomerPairs.begin(); std::vector::iterator the_end_iterator = m_uuidMonomerPairs.end(); while(the_iterator != the_end_iterator) { if((*the_iterator).second.expired() || (*the_iterator).second.lock() == nullptr || !hasMonomer((*the_iterator).second.lock())) the_iterator = m_uuidMonomerPairs.erase(the_iterator); else ++the_iterator; } qDebug() << "At end, count of UUID-Monomer pairs:" << m_uuidMonomerPairs.size(); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CrossLinkedRegion.cpp000664 001750 001750 00000017220 15100504560 026355 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CrossLinkedRegion.hpp" namespace MsXpS { namespace libXpertMassCore { // [3] [4] [5] [6] [9] [11] // o---o---o---o---o--o---o---o---o---o---o---o---o---o---o // | |__| | | | | | // | +-----------+ +-------+ // | | // +------------------+ // // // In the example above, there are two cross-linked regions: [3--9] // and [11--13]. /*! \class MsXpS::libXpertMassCore::CrossLinkedRegion \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CrossLinkedRegion.hpp \brief The CrossLinkedRegion class provides abstractions to work with cross-links and detect the region of oligomers that are linked together. In the following representation, a polymer sequence has a number of CrossLink instances that are intertwined: \code [3] [4] [5] [6] [9] [11] [13] o---o---o---o---o--o---o---o---o---o---o---o---o---o---o | |__| | | | | | | +-----------+ +-------+ | | +------------------+ \endcode In this example, there are two cross-linked regions: [3--9] and [11--13]. The first cross-linked region has a start index of 3 and a stop index of 9 and involves three cross-links. The second cross-linked region only involves a single cross-link involving two monomers at indices 11 and 13. This class is most useful when performing fragmentations of Oligomer instances. */ /*! \variable MsXpS::libXpertMassCore::CrossLinkedRegion::m_startIndex \brief The start index of this cross-linked region. */ /*! \variable MsXpS::libXpertMassCore::CrossLinkedRegion::m_stopIndex \brief The stop index of this cross-linked region. */ /*! \variable MsXpS::libXpertMassCore::CrossLinkedRegion::m_crossLinks \brief The CrossLink instances that are contained in this CrossLinkedRegion instance. */ /*! \brief */ CrossLinkedRegion::CrossLinkedRegion() { m_startIndex = 0; m_stopIndex = 0; } /*! \brief Constructs a CrossLinkedRegion instance with indices \a index_start and \a index_stop. */ CrossLinkedRegion::CrossLinkedRegion(std::size_t index_start, std::size_t index_stop) { m_startIndex = index_start; m_stopIndex = index_stop; } /*! \brief Constructs a CrossLinkedRegion instance as a copy of \a other. The copy of the CrossLink instances is shallow (the shared pointers are copied). */ CrossLinkedRegion::CrossLinkedRegion(const CrossLinkedRegion &other) : m_startIndex(other.m_startIndex), m_stopIndex(other.m_stopIndex), m_crossLinks(other.m_crossLinks) { } /*! \brief Destructs a CrossLinkedRegion instance. */ CrossLinkedRegion::~CrossLinkedRegion() { } /*! \brief Sets the start index of this instance to \a index. */ void CrossLinkedRegion::setStartIndex(std::size_t index) { m_startIndex = index; } /*! \brief Returns the start index of this instance. */ std::size_t CrossLinkedRegion::getStartIndex() { return m_startIndex; } /*! \brief Sets the stop index of this instance to \a index. */ void CrossLinkedRegion::setStopIndex(std::size_t index) { m_stopIndex = index; } /*! \brief Returns the stop index of this instance. */ std::size_t CrossLinkedRegion::getStopIndex() { return m_stopIndex; } /*! \brief Returns a const reference to the container of CrossLink instances. */ const std::vector & CrossLinkedRegion::getCrossLinksCstRef() const { return m_crossLinks; } /*! \brief Returns a reference to the container of CrossLink instances. */ std::vector & CrossLinkedRegion::getCrossLinksRef() { return m_crossLinks; } /*! \brief Adds a the \a cross_link_sp CrossLink to the back of the CrossLink container. Returns the size of the container of CrossLink instances. */ std::size_t CrossLinkedRegion::appendCrossLink(const CrossLinkSPtr &cross_link_sp) { if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; // Only append the cross-link if it is not already in the list. std::vector::const_iterator the_iterator_cst = std::find_if(m_crossLinks.cbegin(), m_crossLinks.cend(), [&cross_link_sp](const CrossLinkSPtr &iter_cross_link_sp) { return iter_cross_link_sp == cross_link_sp; }); if(the_iterator_cst == m_crossLinks.cend()) m_crossLinks.push_back(cross_link_sp); return m_crossLinks.size(); } /*! \brief Adds the CrossLink item in the \a cross_links container to the back of the member CrossLink container. Returns the size of the container of CrossLink instances. */ std::size_t CrossLinkedRegion::appendCrossLinks( const std::vector &cross_links) { for(const CrossLinkSPtr &cross_link_sp : cross_links) m_crossLinks.push_back(cross_link_sp); return m_crossLinks.size(); } /*! \brief Removes the \a cross_link_sp CrossLink item from the member CrossLink container. Returns the size of the container of CrossLink instances. */ std::size_t CrossLinkedRegion::removeCrossLink(CrossLinkSPtr &cross_link_sp) { std::vector::iterator the_iterator = std::find_if(m_crossLinks.begin(), m_crossLinks.end(), [&cross_link_sp](const CrossLinkSPtr &iter_cross_link_sp) { return iter_cross_link_sp == cross_link_sp; }); if(the_iterator != m_crossLinks.end()) m_crossLinks.erase(the_iterator); return m_crossLinks.size(); } /*! \brief Removes the CrossLink at \a index from the member CrossLink container. Returns the size of the container of CrossLink instances. */ std::size_t CrossLinkedRegion::removeCrossLinkAt(std::size_t index) { if(index >= m_crossLinks.size()) qFatalStream() << "Programming error. Index is out of bounds."; m_crossLinks.erase(m_crossLinks.begin() + index); return m_crossLinks.size(); } /*! \brief Assigns \a other to this instance. The copy of the CrossLink instances is shallow (the shared pointers are copied). */ CrossLinkedRegion & CrossLinkedRegion::operator=(const CrossLinkedRegion &other) { if(this == &other) return *this; m_startIndex = other.m_startIndex; m_stopIndex = other.m_stopIndex; m_crossLinks.assign(other.m_crossLinks.begin(), other.m_crossLinks.end()); return *this; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/CrossLinker.cpp000664 001750 001750 00000077715 15100504560 025246 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/CrossLinker.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::CrossLinker \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile CrossLinker.hpp \brief The CrossLinker class provides abstractions to define the chemical basis of a cross-linking reaction. The notion of a cross-linker is that it is the description of the chemical reaction that is carried out by \l{Monomer}s in a \l Polymer or in an \l Oligomer sequence to form a \l CrossLink. There are two different kinds of chemical entities potentially involved in the description of a CrossLinker: \list \a Modif instances that are applied to \l Monomer instances. \a the formula that completes the chemical reactions described by the \l Modif instances. \endlist */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::mcsp_polChemDef \brief The reference polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_name \brief The name of the CrossLinker, \"DisulphideBond\", for example. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_formula \brief The formula that must be applied when the cross-link occurs. \note This formula can be empty because the CrossLinker can be fully described, from a chemical standpoint, by listing Modif instances in the member m_modifs container of Modif instances. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_mono \brief The monoisotopic mass of the CrossLinker. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_avg \brief The average mass of the CrossLinker. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_isValid \brief Tells if the CrossLinker is valid. */ /*! \variable MsXpS::libXpertMassCore::CrossLinker::m_modifs \brief The container of \l Modif instances that describe the reaction (or the reactions) involved in the formation of a CrossLink between \l{Monomer}s of a \l Polymer (or of an \l Oligomer) sequence. */ /*! \brief Constructs a CrossLinker instance with the \a pol_chem_def_csp PolChemDef. */ CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp) : mcsp_polChemDef(pol_chem_def_csp) { } /*! \brief Constructs a CrossLinker instance starting from an XML \a element and a \a pol_chem_def_csp PolChemDef. The \a version indicates what version of the XML element is to be used. This is the current format: \code CFP-chromophore Chromo-O Chromo-H3 Chromo-H \endcode The rendering of the Modif instances requires that the PolChemDef be available. If this is not the case, the rendering of the CrossLinker is stopped at the first element and false is returned. \sa renderXmlClkElement() */ CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version) : mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing CrossLinker with no PolChemDef."; if(!renderXmlClkElement(element, version)) qCritical() << "Failed to fully render or validate the CrossLinker XML element " "for construction of CrossLinker instance."; } /*! \brief Constructs a CrossLinker instance. \list \li \a pol_chem_def_csp: the polymer chemistry definition (\l PolChemDef). \li \a name: the name of this CrossLinker. \li \a formula: the \l Formula that potentially complements the description of the reaction that is the basis of this CrossLinker (used to initialize the Formula base class). \endlist */ CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &formula, double mono, double avg) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_formula(formula), m_mono(mono), m_avg(avg) { } /*! \brief Constructs a CrossLinker instance as a copy of \a other. */ CrossLinker::CrossLinker(const CrossLinker &other) : mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_formula(other.m_formula), m_mono(other.m_mono), m_avg(other.m_avg) { m_modifs.clear(); std::vector::const_iterator the_iterator_cst = other.m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = other.m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { m_modifs.push_back((*the_iterator_cst)); ++the_iterator_cst; } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The copy constructor instantiated a CrossLink that is not " "valid, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destructs this CrossLinker instance */ CrossLinker::~CrossLinker() { // We do not own the modifications in m_modifList! } /*! \brief Initializes this CrossLinker instance using \a element, according to \a version. Returns true if the rendering of \a element was successful, false otherwise. \sa renderXmlClkElement() */ bool CrossLinker::initialize(const QDomElement &element, int version) { if(!renderXmlClkElement(element, version)) { qCritical() << "Failed to fully render or validate the CrossLinker XML element " "for construction of CrossLinker instance."; return false; } return true; } //////////////// THE POLCHEMDEF ///////////////////// /*! \brief Sets the PolChemDef to \a pol_chem_def_csp. */ void CrossLinker::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the CrossLinker with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the PolChemDef. */ const PolChemDefCstSPtr & CrossLinker::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the \a name of the CrossLinker. */ void CrossLinker::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the CrossLinker with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ QString CrossLinker::getName() const { return m_name; } //////////////// THE FORMULA ///////////////////// /*! \brief Sets the formula to \a formula_string. */ void CrossLinker::setFormula(const QString &formula_string) { m_formula = formula_string; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the CrossLinker with errors:\n" << Utils::joinErrorList(error_list, ", "); } /* \brief Returns the formula of this CrossLinker. */ const QString & CrossLinker::getFormula() const { return m_formula; } //////////////// THE MODIFICATIONS ///////////////////// /*! \brief Returns a constant reference to the container of Modif instances. */ const std::vector & CrossLinker::getModifsCstRef() const { return m_modifs; } /*! \brief Returns a reference to the container of Modif instances. */ std::vector & CrossLinker::getModifsRef() { return m_modifs; } /*! \brief Sets the Modif at index \a index to \a modif_csp. \a index cannot be out-of-bounds (fatal error). Returns true. */ bool CrossLinker::insertModifAt(ModifCstSPtr modif_csp, std::size_t index) { if(index >= m_modifs.size()) qFatalStream() << "The index is out of bounds."; if(modif_csp == nullptr || modif_csp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr"; m_modifs.insert(m_modifs.cbegin() + index, modif_csp); return true; } /*! \brief Adds \a modif_csp to this CrossLinker instance. Returns true. */ bool CrossLinker::appendModif(ModifCstSPtr modif_csp) { if(modif_csp == nullptr || modif_csp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr"; m_modifs.push_back(modif_csp); return true; } /*! \brief Returns the \l Modif instance at \a index. \a index cannot be out-of-bounds. */ ModifCstSPtr CrossLinker::getModifAt(std::size_t index) const { if(index >= m_modifs.size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_modifs.at(index); } /*! \brief Removes the \l Modif instance at \a index. \a index cannot be out-of-bounds. Returns true. */ bool CrossLinker::removeModifAt(std::size_t index) { if(index >= m_modifs.size()) qFatalStream() << "Programming error. Index is out of bounds."; m_modifs.erase(m_modifs.cbegin() + index); return true; } /*! \brief Returns an iterator to the found \l Modif instance by \a modif_name in the container. */ bool CrossLinker::hasModif(const QString &modif_name) { std::vector::const_iterator the_iterator_cst = std::find_if( m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) { return modif_csp->getName() == modif_name; }); return the_iterator_cst != m_modifs.cend(); } /*! \brief Returns the index of the found \l Modif instance by \a modif_name in the container, -1 otherwise. */ int CrossLinker::modifIndex(const QString &modif_name) { std::vector::const_iterator the_iterator_cst = std::find_if( m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) { return modif_csp->getName() == modif_name; }); if(the_iterator_cst == m_modifs.cend()) return -1; return std::distance(m_modifs.cbegin(), the_iterator_cst); } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this CrossLinker instance. Returns a reference to this CrossLinker instance. */ CrossLinker & CrossLinker::operator=(const CrossLinker &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_formula = other.m_formula; m_mono = other.m_mono; m_avg = other.m_avg; m_modifs.clear(); std::vector::const_iterator the_iterator_cst = other.m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = other.m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { m_modifs.push_back((*the_iterator_cst)); ++the_iterator_cst; } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The assignment operator copying produced a CrossLink that is not " "valid, with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if this CrossLinker and \a other are identical. */ bool CrossLinker::operator==(const CrossLinker &other) const { if(&other == this) return true; // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). if(m_name != other.m_name || m_formula != other.m_formula || m_mono != other.m_mono || m_avg != other.m_avg || m_modifs.size() != other.m_modifs.size()) return false; for(std::size_t iter = 0; iter < m_modifs.size(); ++iter) { if(*m_modifs.at(iter).get() != *other.m_modifs.at(iter).get()) { qDebug() << "At least one Modif instance differs in both CrossLinker " "instances."; return false; } } return true; } /*! \brief Returns true if this CrossLinker and \a other are different. Returns the negated result of operator==(). */ bool CrossLinker::operator!=(const CrossLinker &other) const { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns the CrossLinker instance from the polymer chemistry definition registered in this instance. The key to search the CrossLinker is this instance's member name. If there is no PolChemDef available, nullptr is returned. If no CrossLinker instance is found by this instance's name, nullptr is returned. */ const CrossLinkerCstSPtr CrossLinker::getFromPolChemDefByName() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_name.isEmpty()) return nullptr; std::vector::const_iterator the_iterator_cst = std::find_if(mcsp_polChemDef->getCrossLinkersCstRef().cbegin(), mcsp_polChemDef->getCrossLinkersCstRef().cend(), [&](const auto &cross_linker_csp) { return cross_linker_csp->getName() == m_name; }); if(the_iterator_cst == mcsp_polChemDef->getCrossLinkersCstRef().cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns the status of this CrossLinker instance the polymer chemistry definition registered in this instance. The key to search the CrossLinker is this instance's member name. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no CrossLinker instance is found by this instance's name, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus CrossLinker::isKnownByNameInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getCrossLinkerCstSPtrByName(m_name) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; }; /*! \brief Returns true if this CrossLinker instance validates successfully, false otherwise. The validation involves the following: \list \li The member polymer chemistry definition must exist. \li The name cannot be empty. \li The formula (if not empty) must validate successfully against the member polymer chemistry definition. \li The list of \l Modif instances must contain either no or more than two Modif instances. In the latter case, the Modif instances must be found in the member polymer chemistry definition and must validate successfully. \endlist When an error occurs, the message is stored in \a error_list_p. */ bool CrossLinker::validate(ErrorList *error_list_p) const { Q_ASSERT(error_list_p != nullptr); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr || !mcsp_polChemDef->getIsotopicDataCstSPtr()->size()) { error_list_p->push_back("The PolChemDef or IsotopicData are not available"); qCritical() << "The polymer chemistry definition member datum is nullptr or " "its isotopic data are be either nullptr or empty. CrossLinker " "validation " "failed."; m_isValid = false; return m_isValid; } if(m_name.isEmpty()) { qCritical() << "The CrossLinker name is empty."; error_list_p->push_back("The CrossLinker name is empty"); } // Remember that the formula of the crosslinker might be empty because the // cross-linker might be fully accounted for by the modifications (below). if(!m_formula.isEmpty()) { Formula temp_formula(m_formula); if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "The CrossLinker formula failed to validate."; error_list_p->push_back("The CrossLinker formula failed to validate"); } } double mono = 0.0; double avg = 0.0; if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg)) { qCritical() << "Failed to calculate the CrossLinker masses."; error_list_p->push_back("Failed to calculate the CrossLinker masses"); } // This is mainly a sanity check, as the pointers to Modif in the // list all point to modification objects in the polymer chemistry // definition, which have been validated already... // The validation actually is simple, it might be that there are NO // modifs, or if this is not the case there must be at least // 2. Indeed, either none of the monomers in the crosslink get // modified, or each one has to be(otherwise we cannot know which // modif goes to which monomer). int size = m_modifs.size(); if(size > 0 && size < 2) { qCritical() << "The CrossLinker may have either zero or more at least " "two Modif instances."; error_list_p->push_back( "The CrossLinker may have either zero or at least two Modif instances"); m_isValid = false; return m_isValid; } for(const ModifCstSPtr &modif_csp : m_modifs) { // The modif must be in the context of our PolChemDef. if(modif_csp->getPolChemDefCstSPtr() != mcsp_polChemDef) { qCritical() << "One Modif:" << modif_csp->getName() << "has not the same PolChemDef as that of this CrossLinker."; error_list_p->push_back(QString("One Modif: %1 has not the same " "PolChemDef as that of this CrossLinker") .arg(modif_csp->getName())); m_isValid = false; return m_isValid; } if(!modif_csp->validate(error_list_p)) { qCritical() << "One Modif:" << modif_csp->getName() << "failed to validate successfully."; error_list_p->push_back( QString("One Modif: %1 ailed to validate successfully") .arg(modif_csp->getName())); m_isValid = false; return m_isValid; } } m_isValid = (error_list_p->size() ? false : true); return m_isValid; } /*! \brief Returns the validity status of this CrossLinker. */ bool CrossLinker::isValid() const { return m_isValid; } //////////////// MASS OPERATIONS ///////////////////// /*! \brief Returns true if the mass calculations for this CrossLinker instance are performed without error, false otherwise. The calculation involved accounting masses for the Modifs (if any) and for the Formula (if non-empty). The masses calculated are set to \a mono and \a avg. The calculations are performed using reference data in \a isotopic_data_csp. */ bool CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; m_isValid = false; return m_isValid; } } // qDebug() << "Calculating masses for the cross-link"; mono = 0; avg = 0; bool ok; if(!m_formula.isEmpty()) { // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg); if(!ok) { qCritical() << "Failed accounting masses for CrossLinker:" << m_name << "and formula:" << m_formula; m_isValid = false; return m_isValid; } } // Now, for each modif in the crossLinker, have to account their // mass. std::vector::const_iterator the_iterator_cst = m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { (*the_iterator_cst)->accountMasses(mono, avg); ++the_iterator_cst; } // qDebug() << "At this point, the masses of the CrossLinker are:" << m_mono //<< "/" << m_avg; return true; } /*! \brief Returns true if the mass calculations for this CrossLinker instance are performed without error, false otherwise. The calculation involved accounting masses for the Modifs (if any) and for the Formula (if non-empty). The member masses (m_mono and m_avg) are updated. The reference data for the computations are accessed at \a isotopic_data_csp. */ bool CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp) { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; m_isValid = false; return m_isValid; } } // qDebug() << "Calculating masses for the cross-link"; m_mono = 0; m_avg = 0; bool ok; if(!m_formula.isEmpty()) { // Formula temp_formula(m_formula); Formula(m_formula).accountMasses( ok, local_isotopic_data_csp, m_mono, m_avg); if(!ok) { qCritical() << "Failed accounting masses for CrossLinker:" << m_name << "and formula:" << m_formula; m_isValid = false; return m_isValid; } } // Now, for each modif in the crossLinker, have to account their // mass. std::vector::const_iterator the_iterator_cst = m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { (*the_iterator_cst)->accountMasses(m_mono, m_avg); ++the_iterator_cst; } // qDebug() << "At this point, the masses of the CrossLinker are:" << m_mono //<< "/" << m_avg; return true; } /*! \brief Increases \a mono_p and \a avg_p by the corresponding member masses first compounded by \a times. Returns this CrossLinker. */ const CrossLinker & CrossLinker::accountMasses(double *mono_p, double *avg_p, int times) const { // qDebug() << "Accounting masses for cross-linker -- mono:" << m_mono //<< "avg:" << m_avg << "by" << times << "times."; if(mono_p) *mono_p += m_mono * times; if(avg_p) *avg_p += m_avg * times; return *this; } /*! \brief Increases \a mono and \a avg by the corresponding member masses first compounded by \a times. Returns this CrossLinker. */ const CrossLinker & CrossLinker::accountMasses(double &mono, double &avg, int times) const { // qDebug() << "Accounting masses for cross-linker -- mono:" << m_mono //<< "avg:" << m_avg << "by" << times << "times."; mono += m_mono * times; avg += m_avg * times; return *this; } /*! \brief Returns the mass of the type defined by \a mass_type. */ double CrossLinker::getMass(Enums::MassType mass_type) const { if(mass_type == Enums::MassType::MONO) return m_mono; else if(mass_type == Enums::MassType::AVG) return m_avg; qFatalStream() << "Mass cannot be anything else than Enums::MassType::MONO or Enums::MassType::AVG."; return -1; } //////////////// XML ///////////////////// /*! \brief Returns true if parsing of XML \a element, using a \a{version}ed function was successful, false otherwise. The data in \a element are set to this CrossLinker instance if they validated successfully, thus essentially initializing this CrossLinker instance. The format of the element is: \code CFP-chromophore Chromo-O Chromo-H3 Chromo-H \endcode */ bool CrossLinker::renderXmlClkElement(const QDomElement &element, [[maybe_unused]] int version) { if(element.tagName() != "clk") return false; QDomElement child; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); if(m_name.isEmpty()) return false; // qDebug() << "The crosslinker name:" << m_name; child = child.nextSiblingElement("formula"); if(child.isNull()) { qCritical() << "The CrossLinker did not render correctly: the " "element tag was not found."; m_isValid = false; return m_isValid; } // Here, it is possible that the formula element be empty because the // crosslinker might be accounted for by using the modifications in it. if(!child.isNull() && !child.text().isEmpty()) { Formula temp_formula; if(!temp_formula.renderXmlFormulaElement(child)) { qCritical() << "The CrossLinker did not render correctly: the formula did not " "render correctly."; m_isValid = false; return m_isValid; } ErrorList error_list; if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), &error_list)) { qCritical() << "The CrossLinker did not render correctly: the formula did not " "render correctly with errors:" << Utils::joinErrorList(error_list); m_isValid = false; return m_isValid; } m_formula = temp_formula.getActionFormula(/*with_title*/ true); // qDebug() << "The formula:" << m_formula; } // At this point we need the PolChemDef to render the modifications, // so if we do not have that, return false. if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "The PolChemDef is unavailable, cannot render any Modif."; return false; } // At this point there might be 0, 1 or more "modifname" elements. child = child.nextSiblingElement("modifname"); #if 0 // Old version while(!child.isNull()) { // qDebug() << "Now handling CrossLinker modif:" << child.text(); int index = Modif::isNameInList(child.text(), refList); if(index == -1) { qDebug() << "Failed to parse one modification of the crosslink:" << m_name; return false; } else { // qDebug() //<< "Found the CrossLinker modification in the reference list:" //<< m_name; } // Modif *modif = mcsp_polChemDef->modifList().at(index); // qDebug() << "The found modif has name:" << modif->name() //<< "and masses:" << modif->mono() << "/" << modif->avg(); m_modifList.append(mcsp_polChemDef->modifList().at(index)); child = child.nextSiblingElement("modifname"); } #endif while(!child.isNull()) { if(child.tagName() != "modifname") { qCritical() << "The CrossLinker did not render correctly: the " " element tag was not found."; return false; } QString modif_name = child.text(); qDebug() << "Currently parsed Modif name:" << modif_name; ModifCstSPtr modif_csp = mcsp_polChemDef->getModifCstSPtrByName(modif_name); if(modif_csp != nullptr) m_modifs.push_back(modif_csp); else { qCritical() << "The CrossLinker references a Modif by name that " "could not be found in the PolChemDef."; return false; } child = child.nextSiblingElement("modifname"); } if(!calculateMasses(nullptr)) { qDebug() << __FILE__ << __LINE__ << "Failed to calculate masses for crossLinker" << m_name; return false; } ErrorList error_list; if(!validate(&error_list)) { qCritical() << "The CrossLinker rendered as an XML element failed " "to validate with errors:" << Utils::joinErrorList(error_list, ", "); m_isValid = false; return m_isValid; } return true; } /*! \brief Formats this CrossLinker instance in a heap-allocated string to be used as an XML element in the polymer chemistry definition. The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a dynamically allocated string that needs to be freed after use. */ QString CrossLinker::formatXmlClkElement(int offset, const QString &indent) const { QString text; int newOffset; int iter = 0; QString lead(""); // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: * Phosphorylation -H+H2PO3 Phosphorylation Acetylation * */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n").arg(lead).arg(m_formula); std::vector::const_iterator the_iterator_cst = m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { text += QString("%1%2\n") .arg(lead) .arg((*the_iterator_cst)->getName()); ++the_iterator_cst; } // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } //////////////// UTILS ///////////////////// /*! \brief Returns a string representing this CrossLinker instance. */ QString CrossLinker::toString() const { QString text; text += QString("%1 [%2]\n").arg(m_name).arg(m_formula); if(m_modifs.size()) { std::vector::const_iterator the_iterator_cst = m_modifs.cbegin(); std::vector::const_iterator the_end_iterator_cst = m_modifs.cend(); while(the_iterator_cst != the_end_iterator_cst) { text += QString("%1,").arg((*the_iterator_cst)->getName()); ++the_iterator_cst; } text += "\n"; } text += "masses: "; text += QString::number(m_mono); text += " - "; text += QString::number(m_avg); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Formula.cpp000664 001750 001750 00000224420 15100504560 024400 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Formula \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Formula.hpp \brief The Formula class provides sophisticated abstractions to work with formulas. There are two peculiarities with this Formula implementation: \list \li The \e{Actionformula} \li The \e{Title} \endlist \e{\b{The action-formula}}: the main textual element in this Formula class is the \e{action-formula} (member m_actionFormula). A formula is the description of the atomic composition of a compound. For example, the string \e{C2H6} is a formula. While the previous \e{C2H6} example describes a static chemical object, a Formula can also describe a dynamic chemical event, like a reaction, by describing what chemical entities are gained by the molecule during the chemical reaction (the "plus" component of the action-formula) and what chemical entities are lost by the molecule (the "minus" component). For example, an acetylation reaction can be described by the loss of \e{H2O} with gain of \e{CH3COOH}. The net chemical gain on the molecule will be \e{CH3CO}. In this example, one would thus define an action-formula in the following way: \e{-H20+CH3COOH}. The "minus" formula associated with the '-' action accounts for the leaving group of the reaction, while the "plus" formula associated with the '+' action accounts for the entering group of the reaction. Note that there is no limitation on the amount of such actions, as one could have an action formula like this \e{-H+CO2-H2O+C2H6}. An \e{action-formula} does not need to have any action sign (+ or -), and if it has no sign, the action-formula is a plus-signed formula by default, which is what the reader would expect for a standard formula. \e{\b{The title}}: the action-formula may be documented with a title: a prefix text enclosed in double quotes, like the following: \e{"Decomposed adenine" C5H4N5 +H}. This documentation element is called the \e{title}. Note that the presence of a title in a formula does not change anything to its workings as long as the \e{title} is effectively enclosed in double quotes. The title is by no means for an action-formula to work correctly. It is mainly used in some particular context, like the calculator. An action-formula behaves exactly the same as a simple formula from an end user perspective. Behind the scenes, functions are called to separate all the '+'-associated formulas from all the '-'-associated formulas so that masses are correctly associated to each "leaving" or "entering" chemical groups. Formulas that are '-'-associated are stored in the so-called "minus formula", while '+'-associated ones are stored in the "plus formula". Note that all the formulas in Formula are QString objects. Upon parsing of the action-formula, the m_minusFormula and the m_plusFormula members are populated with formulas (in the \e{-H+CO2-H2O+C2H6} example, the "minus formula" would contain "HH2O", while the "plus formula" would contain "CO2C2H6") and these are next used to account for the net formula. \note A Formula instance is created in an invalid state (m_isValid is false). Only when all the relevant data have been set and the Formula is validated explicitely (\l{validate()}), the user of the Formula knows it is valid or not. */ /*! \enum MsXpS::libXpertMassCore::Formula::SplitResult This enum type specifies the result of an action-formula parsing process: \value NOT_SET The value was not set \value FAILURE The splitting work failed \value HAS_PLUS_COMPONENT The action formula has a plus component \value HAS_MINUS_COMPONENT The action formula has a minus component \value HAS_BOTH_COMPONENTS The action formula has both plus and minus components */ /*! \variable MsXpS::libXpertMassCore::Formula::m_title \brief String representing the title of the action-formula. The title is the descriptive string in double quotes that is associated to a formula, like this: \c "Acetylation"-H2O+CH3COOH */ /*! \variable MsXpS::libXpertMassCore::Formula::m_actionFormula \brief String representing the action-formula. */ /*! \variable MsXpS::libXpertMassCore::Formula::m_plusFormula \brief String representing the "plus" component of the main m_actionFormula. This member datum is set upon parsing of m_actionFormula. */ /*! \variable MsXpS::libXpertMassCore::Formula::m_minusFormula \brief String representing the "minus" component of the main m_minusFormula. This member datum is set upon parsing of m_actionFormula. */ /*! \variable MsXpS::libXpertMassCore::Formula::m_symbolCountMap \brief Map relating the symbols (as keys) found in the formula and their counts (atoms, in fact, as values). Note that the count value type is double, which allows for interesting things to be done with Formula. Also, the count value might be negative if the net mass of an action-formula is negative. \sa Formula::splitActionParts() */ /*! \variable int MsXpS::libXpertMassCore::Formula::m_forceCountIndex \brief The m_forceCountIndex tells if when defining a chemical composition formula, the index '1' is required when the count of a symbol is not specified and thus considered to be '1' by default. If true, water should be described as "H2O1", if false, it might be described as "H2O". */ /*! \variable MsXpS::libXpertMassCore::Formula::m_isValid \brief The status of the formula. */ /*! \brief Constructs a Formula instance. */ Formula::Formula(QObject *parent): QObject(parent) { } /*! \brief Constructs a Formula instance using the XML \a element according to the \a version. */ Formula::Formula(const QDomElement &element, int version, QObject *parent) : QObject(parent) { bool result = renderXmlFormulaElement(element, version); if(!result) qCritical() << "Failed rendering XML element for construction of Formula instance."; } /*! \brief Constructs a formula initialized with the \a formula_string action-formula string. \a formula_string needs not be an action-formula, but it might be an action-formula. This formula gets copied into the \c m_actionFormula without any processing afterwards. The default status of the Formula (m_isValid) is let to false because the formula cannot be validated without reference isotopic data. */ Formula::Formula(const QString &formula_string, QObject *parent) : QObject(parent), m_actionFormula{formula_string} { m_title = removeTitle(); // qDebug() << "The title:" << m_title; removeSpaces(); // qDebug() << "Formula after removing spaces:" << m_actionFormula; if(m_actionFormula.isEmpty()) { qCritical() << "Formula created empty."; } if(!checkSyntax()) { qCritical() << "Formula constructed with an action formula that does not " "pass the checkSyntax test."; } // Because there has been no validation with IsotopicData, the default // false value is maintained for m_isValid. } /*! \brief Constructs a formula as a copy of \a other. The copy is deep with \e{all} the data copied from \a other to the new formula. There is no processing afterwards. */ Formula::Formula(const Formula &other, QObject *parent) : QObject(parent), m_title(other.m_title), m_actionFormula(other.m_actionFormula), m_plusFormula(other.m_plusFormula), m_minusFormula(other.m_minusFormula), m_symbolCountMap(other.m_symbolCountMap), m_isValid(other.m_isValid) { // GCOV_EXCL_START // Sanity check if(!removeTitle().isEmpty()) { qCritical() << "The formula string still has a title."; m_isValid = false; } // GCOV_EXCL_STOP removeSpaces(); if(m_actionFormula.isEmpty()) { qCritical() << "Copy-constructed Formula created with empty action-formula."; m_isValid = false; } if(!checkSyntax()) { qCritical() << "Formula constructed with an action formula that does not pass the " "checkSyntax test."; m_isValid = false; } } /*! \brief Destructs this formula. There is nothing to be delete explicitly. */ Formula::~Formula() { } /*! \brief Return a newly allocated Formula that is initialized using \a other and setting its parent to \a parent. */ Formula * Formula::clone(const Formula &other, QObject *parent) { Formula *copy_p = new Formula(parent); copy_p->initialize(other); return copy_p; } /*! \brief Return a reference to this Formula after having initialized it using \a other. */ Formula & Formula::initialize(const Formula &other) { if(&other == this) return *this; m_title = other.m_title; m_actionFormula = other.m_actionFormula; m_plusFormula = other.m_plusFormula; m_minusFormula = other.m_minusFormula; m_symbolCountMap = other.m_symbolCountMap; m_isValid = other.m_isValid; // GCOV_EXCL_START // Sanity check if(!removeTitle().isEmpty()) { qCritical() << "The formula string still has a title."; m_isValid = false; } // GCOV_EXCL_STOP removeSpaces(); if(m_actionFormula.isEmpty()) { qCritical() << "Formula created empty."; m_isValid = false; } if(!checkSyntax()) { qCritical() << "Formula set to an action formula that does not pass the " "checkSyntax test."; m_isValid = false; } return *this; } //////////////// THE ACTIONFORMULA ///////////////////// /*! Sets the action-formula \a formula to this Formula. The \a formula is copied to this m_actionFormula. Since the new formula was not validated, the status of this formula (m_isValid) is set to false. No other processing is performed afterwards. */ void Formula::setActionFormula(const QString &formula) { m_actionFormula = formula; m_title = removeTitle(); removeSpaces(); if(m_actionFormula.isEmpty()) { qCritical() << "Formula set to an empty string."; } if(!checkSyntax()) qCritical() << "Formula set to an action formula that does not pass the " "checkSyntax test."; m_isValid = false; } /*! Sets the action-formula from \a formula to this Formula. The action-formula from \a formula is copied to this m_actionFormula. Since the new formula was not validated, the status of this formula (m_isValid) is set to false. No other processing is performed afterwards. */ void Formula::setActionFormula(const Formula &formula) { m_actionFormula = formula.m_actionFormula; m_title = removeTitle(); if(m_actionFormula.isEmpty()) { qCritical() << "Formula set to an empty string."; } if(!checkSyntax()) qCritical() << "Formula set to an action formula that does not pass the " "checkSyntax test."; m_isValid = false; } /*! \brief Appends to this formula the \a action_formula. The \a action_formula string is first stripped of its whitespace with QString::simplified(). Then it is appended to the m_actionFormula only if: \list \li It is not empty (if empty the function does nothing and returns false) \li It passes the checkSyntax() test (if not, the function does nothing and returns false) \endlist The status of this Formula is set to false because there was not explicit validation done. The function returns false if nothing was changed and true if something was actually appended to m_actionFormula. */ bool Formula::appendActionFormula(const QString &action_formula) { QString local_action_formula = action_formula.simplified(); if(local_action_formula.isEmpty()) return false; // Remove the spaces before appending. local_action_formula.remove(QRegularExpression("\\s+")); // Removes the title from the action-formula Formula temp_formula(local_action_formula); if(!temp_formula.checkSyntax()) { qCritical() << "Cannot append formula that does not pass the checkSyntax test."; return false; } else { // We append the action-formula stripped of its potential title. m_actionFormula.append(temp_formula.getActionFormula()); } // The formula has not been validated formally. m_isValid = false; return true; } /*! \brief Returns the action formula, along the the title if \a with_title is true. */ QString Formula::getActionFormula(bool with_title) const { // qDebug() << "The title is:" << m_title; if(with_title && !m_title.isEmpty()) return QString("\"%1\"%2").arg(m_title).arg(m_actionFormula); return m_actionFormula; } //////////////// THE TITLE ///////////////////// /*! \brief Sets the title leading component of a formula to \a title. A fully self-described formula might look like this: "Acetylation"+CH3COOH-H2O The first string between double quotes is called the title. */ void Formula::setTitle(const QString &title) { m_title = title; } /*! \brief Returns the "title" leading component of a formula. A fully selfdescribed formula might look like this: "Acetylation"+CH3COOH-H2O The first string between quotes is called the title. */ QString Formula::getTitle() const { return m_title; } /*! \brief Returns the title from the member action-formula. The \e{title} of a formula is the string, enclosed in double quotes, that is located in front of the actual chemical action-formula. This function removes that \e{title} string from the member action-formula using a QRegularExpression. */ QString Formula::extractTitle() const { QRegularExpression regexp("^\"(.*)\""); QRegularExpressionMatch match = regexp.match(m_actionFormula); if(!match.hasMatch()) { // qDebug() << "Has no match."; return QString(); } // qDebug() << "Match:" << match.captured(0); return match.captured(1); } /*! \brief Removes the title from m_actionFormula and returns it. The \e{title} of a formula is the string, enclosed in double quotes, that is located in front of the actual chemical action-formula. This function removes that \e{title} string from the member action-formula using a QRegularExpression. The caller may use the returned title string to set it to m_title. \sa Formula::extractTitle(), Formula::setTitle() */ QString Formula::removeTitle() { QString title = extractTitle(); if(!title.isEmpty()) { QRegularExpression regexp("^(\".*\")"); m_actionFormula = m_actionFormula.remove(regexp); } return title; } //////////////// THE ATOM INDEX LOGIC ///////////////////// /*! \brief Sets m_forceCountIndex to \a forceCountIndex. When a formula contains a chemical element in a single copy, it is standard practice to omit the count index: H2O is the same as H2O1. If forceCountIndex is true, then the formula has to be in the form H2O1. This is required for some specific calculations. The status (m_isValid) is set to false. */ void Formula::setForceCountIndex(bool forceCountIndex) { m_forceCountIndex = forceCountIndex; } /*! \brief Returns true if single atoms should have a count index, false otherwise. The force count index is set to true, when a formula needs to have atom indices set even if the count for these atoms is 1. For example, H2O1 as compared to H2O. */ bool Formula::isForceCountIndex() const { return m_forceCountIndex; } //////////////// THE SYNTAX CHECKING LOGIC ///////////////////// #if 0 Old version that parsed the action-formula char by char. bool Formula::checkSyntax(const QString &formula, bool forceCountIndex) { // Static function. // qDebug() << "Checking syntax with formula:" << formula; QChar curChar; bool gotUpper = false; bool wasSign = false; bool wasDigit = false; // Because the formula that we are analyzing might contain a title // and spaces , we first remove these. But make a local copy of // the member datum. QString localFormula = formula; // One formula can be like this: // "Decomposed adenine" C5H4N5 +H // The "Decomposed adenine" is the title // The C5H4N5 +H is the formula. localFormula.remove(QRegularExpression("\".*\"")); // We want to remove all the possibly-existing spaces. localFormula.remove(QRegularExpression("\\s+")); for(int iter = 0; iter < localFormula.length(); ++iter) { curChar = localFormula.at(iter); // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()" //<< "Current character:" << curChar; // FIXME One improvement that would ease modelling the Averagine would // be to silently allow double formula indices (that is, double atom // counts). They would not be compulsory if(curChar.category() == QChar::Number_DecimalDigit) { // We are parsing a digit. // We may not have a digit after a +/- sign. if(wasSign) return false; wasSign = false; wasDigit = true; continue; } else if(curChar.category() == QChar::Letter_Lowercase) { // Current character is lowercase, which means we are inside // of an atom symbol, such as Ca(the 'a') or Nob(either // 'o' or 'b'). Thus, gotUpper should be true ! if(!gotUpper) return false; // We may not have a lowercase character after a +/- sign. if(wasSign) return false; // Let the people know that we have parsed a lowercase char // and not a digit. wasSign = false; wasDigit = false; } else if(curChar.category() == QChar::Letter_Uppercase) { // Current character is uppercase, which means that we are // at the beginning of an atom symbol. // There are two cases: // 1. We are starting for the very beginning of the formula, and // nothing came before this upper case character. That's fine. // 2. We had previously parsed a segment of the formula, and in this // case, we are closing a segment. If the parameter // obligatoryCountIndex is true, then we need to ensure that the // previous element had an associated number, even it the count // element is 1. This is required for the IsoSpec stuff in the gui // programs. if(iter > 0) { if(forceCountIndex) { if(!wasDigit) { qDebug() << "Returning false because upper case char was not" "preceded by digit while not at the first char of " "the formula"; return false; } } } // Let the people know what we got: wasSign = false; gotUpper = true; wasDigit = false; } else { if(curChar != '+' && curChar != '-') return false; else { // We may not have 2 +/- signs in a raw. if(wasSign) return false; } wasSign = true; gotUpper = false; wasDigit = false; } } // end for (int iter = 0 ; iter < localFormula.length() ; ++iter) // Note that if we want an obligatory count index, then, at the end of the // formula, *compulsorily* we must have parsed a digit. if(forceCountIndex && !wasDigit) { qDebug() << "Returning false because the formula does not end with a digit."; return false; } // At this point we found no error condition. return true; } #endif /*! \brief Returns true if the member action-formula is syntactically valid, false otherwise. \sa checkSyntax(const QString &formula, bool force_count_index) */ bool Formula::checkSyntax() const { // The default formula is always m_actionFormula. return checkSyntax(m_actionFormula, m_forceCountIndex); } /*! \brief Returns true if the \a formula_string action-formula is syntactically valid, false otherwise. If \a force_count_index is true, the syntax check accounts for the requirement that all the symbols in the formula must be indexed, even if that symbol's count is 1. This means that H2O would not pass the check, while H2O1 would. The formula is first stripped of its title (if any), then all the spaces are removed. MsXpS::libXpertMassCore::Formula::subFormulaRegExp is then used to extract each "plus" and / or "minus" component while checking its syntactic validity. \note The syntax checking code does not verify that the action-formula is chemically valid, that is, the "Cz4" symbol / count pair would check even if the Cz chemical element does not exist. \sa validate() */ bool Formula::checkSyntax(const QString &formula_string, bool force_count_index) { // qDebug() << "Checking syntax of formula string" << formula_string; // Because the formula that we are analyzing might contain a title // and spaces , we first remove these. But make a local copy of // the member datum. QString local_formula_string = formula_string; // One formula can be like this: // "Decomposed adenine" C5H4N5 +H // The "Decomposed adenine" is the title // The C5H4N5 +H is the formula. local_formula_string.remove(QRegularExpression("\".*\"")); // We want to remove all the possibly-existing spaces. local_formula_string.remove(QRegularExpression("\\s+")); if(local_formula_string.isEmpty()) { qWarning() << "The action formula string is empty."; return false; } // qDebug() << "The formula is:" << local_formula_string; // The raw formula might include: // +/- sign before the symbol // then the symbol (one uppercase any lowercase) // then the count as an integer or a double. // Attention, the regular expression logic below works by finding // patterns that match the regexp, BUT that does not means that // spurious substrings at the beginning or at the end cannot be // present and go undetected, like this: "3Cz3H12O6N14L2", where // the '3' on the left is not seen. // We thus need to first ensure that the string never begins with // something else than a +/- sign (optionally) and a letter (uppercase). QRegularExpression start_of_formula("^[+-]?[A-Z]"); QRegularExpressionMatch match = start_of_formula.match(local_formula_string); if(!match.hasMatch()) { qCritical() << "Error at start of formula string" << formula_string; return false; } // Like wise for formulas that do not end either by [A-Z] or [a-z] or \\d. QRegularExpression end_of_formula("[A-Za-z\\d]$"); match = end_of_formula.match(local_formula_string); if(!match.hasMatch()) { qCritical() << "Error at end of formula string."; return false; } for(const QRegularExpressionMatch &match : Utils::subFormulaRegExp.globalMatch(local_formula_string)) { QString full_match = match.captured(0); // qDebug() << "The full sub-match:" << full_match; QString sign = match.captured(1); QString symbol = match.captured(2); QString count_string = match.captured(3); if(!count_string.isEmpty()) { bool ok = false; // Verify that it correctly converts to double. count_string.toDouble(&ok); if(!ok) return false; } else { if(force_count_index) { qCritical() << "Error: symbol" << symbol << "has no index."; return false; } else { // qDebug() << "Symbol" << symbol // << "has no index but that is tolerated."; } } // qDebug() << "Sign:" << match.captured(1) << "Symbol:" << // match.captured(2) // << "Count:" << match.captured(3); } // qDebug() << "Returning true"; // qDebug() << "Checked syntax of formula " << formula_string // << "returning true"; return true; } //////////////// OPERATORS ///////////////////// /*! \brief Initializes all the member data of this formula by copying to it the data from \a other. The copy is deep with \e{all} the data from \a other being copied into this formula. There is no processing afterwards. */ Formula & Formula::operator=(const Formula &other) { if(&other == this) return *this; m_title = other.m_title; m_actionFormula = other.m_actionFormula; m_plusFormula = other.m_plusFormula; m_minusFormula = other.m_minusFormula; m_symbolCountMap = other.m_symbolCountMap; m_isValid = other.m_isValid; // GCOV_EXCL_START // Sanity check if(!removeTitle().isEmpty()) { qCritical() << "The formula string still has a title."; m_isValid = false; } // GCOV_EXCL_STOP removeSpaces(); if(m_actionFormula.isEmpty()) { qCritical() << "Formula created empty."; m_isValid = false; } if(!checkSyntax()) { qCritical() << "Formula set to an action formula that does not pass the " "checkSyntax test."; m_isValid = false; } return *this; } /*! Returns true if this Formula and \a other are identical, false otherwise. The comparison is only performed on the title and action-formula, not on any other member data that actually derive from the processing of the action-formula. */ bool Formula::operator==(const Formula &other) const { if(&other == this) return true; if(m_title != other.m_title || m_actionFormula != other.m_actionFormula) return false; return true; } /*! Returns true if this Formula and \a other are different, false otherwise. Returns the negated result of operator==(). */ bool Formula::operator!=(const Formula &other) const { if(&other == this) return false; return !operator==(other); } //////////////// THE SUB-FORMULAS OPERATIONS ///////////////////// /*! \brief Tells the "plus" ('+') and "minus" ('-') parts in the member action-formula. Parses the m_actionFormula action-formula and separates all the minus components of that action-formula from all the plus components. The different components are set to \a plus_formula and \a minus_formula. At the end of the split work, each sub-formula (\a plus_formula and \a minus_formula) is actually parsed for validity, using the \a isotopic_data_csp IsotopicData as reference. If \a times is not 1, then the accounting of the plus/minus formulas is compounded by this factor. If \a store is true, the symbol/count data obtained while parsing of the plus/minus action-formula components are stored in \a symbol_count_map. If \a reset is true, the symbol/count data in \a symbol_count_map are reset before the parsing operation. Setting this parameter to false may be useful if the caller needs to "accumulate" the accounting of the formulas. The parsing of the action-formula is performed by performing its deconstruction using \l{Utils::subFormulaRegExp}. Returns FormulaSplitResult::FAILURE if the splitting failed, FormulaSplitResult::HAS_PLUS_COMPONENT if at least one of the components of the action-formula was found to be of type plus, FormulaSplitResult::HAS_MINUS_COMPONENT if at least one of the components of the action-formula was found to be of type minus. The result can be an OR'ing of both values (FormulaSplitResult::HAS_BOTH_COMPONENTS) in the m_actionFormula action-formula. Because this function does not modify member data, by writing the results of the computations to the passed variables, it is declared const. If this function completes successfully, then that validates it successfully (syntax ok, and symbols known to the reference isotopic data), and m_isValid is set to true, otherwise m_isValid is set to false. */ Formula::SplitResult Formula::splitActionParts(IsotopicDataCstSPtr isotopic_data_csp, QString &plus_formula, QString &minus_formula, std::map &symbol_count_map, double times, bool store, bool reset) const { if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) { qCritical( "Cannot split action parts of Formula without available IsotopicData."); m_isValid = false; return SplitResult::FAILURE; } SplitResult formula_split_result = SplitResult::NOT_SET; // We are asked to put all the '+' components of the formula // into corresponding formula and the same for the '-' components. plus_formula.clear(); minus_formula.clear(); if(reset) symbol_count_map.clear(); #if 0 // This old version tried to save computing work, but it then anyways // calls for parsing of the formula which is the most computing-intensive // part. Thus we now rely on the regular expression to simultaneously // check the syntax, divide the formula into its '+' and '-' parts, // and finally check that the symbol is known to the isotopic data. ^ // If the formula does not contain any '-' character, then we // can approximate that all the formula is a '+' formula, that is, a // plusFormula: if(actions() == '+') { // qDebug() << "Only plus actions."; plus_formula.append(formula); // At this point we want to make sure that we have a correct // formula. Remove all the occurrences of the '+' sign. plus_formula.replace(QString("+"), QString("")); if(plus_formula.length() > 0) { // qDebug() << "splitActionParts: with plus_formula:" << // plus_formula; if(!parse(isotopic_data_csp, plus_formula, times, store, reset)) return FormulaSplitResult::FAILURE; else return FORMULA_SPLIT_PLUS; } } // End of // if(actions() == '+') // If we did not return at previous block that means there are at least one // '-' component in the formula. we truly have to iterate in the formula... #endif // See the explanations in the header file for the member datum // m_subFormulaRegExp and its use with globalMatch(). One thing that is // important to see, is that the RegExp matches a triad : [ [sign or not] // [symbol] [count] ], so, if we have, says, formula "-H2O", it would match: // First '-' 'H' '2' // Second 'O' // The problem is that at second match, the algo thinks that O1 is a + // formula, while in fact it is part of a larger minus formula: -H2O. So we // need to check if, after a '-' in a formula, there comes or not a '+'. If so // we close the minus formula and start a plus formula, if not, we continue // adding matches to the minus formula. // qDebug() << "Now regex parsing with globalMatch feature of formula:" // << formula; bool was_minus_formula = false; Utils utils; for(const QRegularExpressionMatch &match : Utils::subFormulaRegExp.globalMatch(m_actionFormula)) { QString sub_match = match.captured(0); // qDebug() << "Entering [+-]? sub-match:" << sub_match; QString sign = match.captured(1); QString symbol = match.captured(2); QString count_as_string = match.captured(3); double count_as_double = 1.0; if(!count_as_string.isEmpty()) { bool ok = false; count_as_double = count_as_string.toDouble(&ok); if(!ok) { m_isValid = false; return SplitResult::FAILURE; } } else { count_as_string = "1"; } // Check that the symbol is known to the isotopic data. // qDebug() << "The symbol:" << symbol << "with count:" << // count_as_double; int count = 0; if(!isotopic_data_csp->containsSymbol(symbol, count)) { m_isValid = false; return SplitResult::FAILURE; } // Determine if there was a sign if(sign == "-") { // qDebug() << "Appending found minus formula:" // << QString("%1%2").arg(symbol).arg(count_as_string); minus_formula.append( QString("%1%2").arg(symbol).arg(count_as_string)); formula_split_result |= SplitResult::HAS_MINUS_COMPONENT; if(store) { // qDebug() << "Accounting symbol / count pair:" << symbol << "/" // << count_as_double * static_cast(times); accountSymbolCountPair(symbol_count_map, symbol, -1 * count_as_double * static_cast(times)); // qDebug() << " ...done."; } // Let next round know that we are inside a minus formula group. was_minus_formula = true; } else if(sign.isEmpty() && was_minus_formula) { // qDebug() << "Appending new unsigned formula to the minus formula:" // << QString("%1%2").arg(symbol).arg(count_as_string); minus_formula.append( QString("%1%2").arg(symbol).arg(count_as_string)); if(store) { // qDebug() << "Accounting symbol / count pair:" << symbol << "/" // << count_as_double * static_cast(times); accountSymbolCountPair(symbol_count_map, symbol, -1 * count_as_double * static_cast(times)); // qDebug() << " ...done."; } // Let next round know that we are still inside a minus formula group. was_minus_formula = true; } else // Either there was a '+' sign or there was no sign, but // we were not continuing a minus formula, thus we are parsing // a true '+' formula. { // qDebug() << "Appending found plus formula:" // << QString("%1%2").arg(symbol).arg(count_as_string); plus_formula.append(QString("%1%2").arg(symbol).arg(count_as_string)); formula_split_result |= SplitResult::HAS_PLUS_COMPONENT; if(store) { // qDebug() << "Accounting symbol / count pair:" << symbol << "/" // << count_as_double * static_cast(times); accountSymbolCountPair(symbol_count_map, symbol, count_as_double * static_cast(times)); // qDebug() << " ...done."; } was_minus_formula = false; } } // qDebug() << "Formula" << formula << "splits" //<< "(+)" << plus_formula << "(-)" << minus_formula; m_isValid = true; return formula_split_result; } /*! \brief Tells the "plus" ('+') and "minus" ('-') parts in the member actionformula. Parses the m_actionFormula action-formula and separates all the minus components of that action-formula from all the plus components. The different components are set to their corresponding formula (m_minusFormula and m_plusFormula). This function delegate its work to the other splitActionParts() passing arguments m_plusFormula, m_minusFormula, m_symbolCountMap, such that this function modifies the content of this very object. In all the computations above, reference isotopic data are accessed at \a isotopic_data_csp. If \a times is not 1, then that value is used to compound the results of the computations. If \a store is true, then the results of the computations are stored in this object. If \a reset is true, then the intermediate computation values and objects are reset. */ Formula::SplitResult Formula::splitActionParts(IsotopicDataCstSPtr isotopic_data_csp, double times, bool store, bool reset) { return splitActionParts(isotopic_data_csp, m_plusFormula, m_minusFormula, m_symbolCountMap, times, store, reset); } /*! \brief Calls actions(const QString &formula) on this Formula's action-formula m_actionFormula. Returns '+' if it only contains "plus" elements or '-' if at least one "minus" element was found. If m_actionFormula contains no sign at all, then it is considered to contain only '+' elements and the function returns '+'. If at least one element is found associated to a '-', then the "minus" action prevails and the function returns '-'. \sa actions(const QString &formula), splitActionParts() */ QChar Formula::actions() const { return actions(m_actionFormula); } /*! \brief Returns '+' if \a formula only contains "plus" elements or '-' if at least one "minus" element was found. If \a formula contains no sign at all, then it is considered to contain only '+' elements and the function returns '+'. If at least one element is found associated to a '-', then the "minus" action prevails and the function returns '-'. \sa actions(), splitActionParts() */ QChar Formula::actions(const QString &formula) { double minusCount = formula.count('-', Qt::CaseInsensitive); return (minusCount == 0 ? '+' : '-'); } /*! \brief Returns true if the member "minus" formula component is not empty, false otherwise. */ bool Formula::hasNetMinusPart() { return m_minusFormula.size(); } /*! \brief Returns the m_plusFormula formula. */ QString Formula::getPlusFormula() const { return m_plusFormula; } /* ! No doc \brief Returns the m_minusFormula formula. */ QString Formula::getMinusFormula() const { return m_minusFormula; } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns true if the formula validates successfully, false otherwise. The validation uses the \a isotopic_data_csp reference data and involves: \list \li Checking that the member action-formula is not empty and has a proper syntax. Returns false otherwise; \li Splitting the action-formula into a plus_formula and a minus_formula components using \l{splitActionParts()}). If that step fails, returns false; \li Verifying that both the plus_formula and the minus_formula are not empty. Returns false otherwise. \endlist If errors are encountered, meaningful messages are stored in \a error_list_p (which is not cleared). If the validation is successful, m_isValid is set to true, otherwise it is set to false. */ bool Formula::validate(IsotopicDataCstSPtr isotopic_data_csp, ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) { qCritical() << "Cannot validate a Formula if the isotopic data are unavailable."; error_list_p->push_back( "Cannot validate a Formula if the isotopic data are unavailable"); } // qDebug() << "isotopic_data_csp.get():" << isotopic_data_csp.get(); if(m_actionFormula.isEmpty()) { qCritical() << "Cannot validate a Formula that is empty."; error_list_p->push_back("Cannot validate a Formula that is empty"); } if(!checkSyntax()) { qCritical() << "Cannot validate a Formula that fails the syntax check test."; error_list_p->push_back( "Cannot validate a Formula that fails the syntax check test"); } // qDebug() << "Now splitting formula" << m_actionFormula << "into its action // parts."; QString plus_formula; QString minus_formula; std::map symbol_count_map; SplitResult result = splitActionParts(isotopic_data_csp, plus_formula, minus_formula, symbol_count_map, 1, /*store*/ false, /*reset*/ true); if(result == SplitResult::FAILURE) { qCritical() << "Failed splitting the Formula."; error_list_p->push_back("Failed splitting the Formula"); } // Both the action formulas cannot be empty. if(!plus_formula.size() && !minus_formula.size()) { qCritical() << "Both the plus and minus formulas are empty."; error_list_p->push_back("Both the plus and minus formulas are empty"); } // If we added errors, then that means that the Monomer was not valid. m_isValid = (error_list_p->size() > error_count ? false : true); return m_isValid; } /*! \brief Returns true if the formula validates successfully, false otherwise. See the other validation function for the validation logic. This function allows to store in member data the results of the validation process if \a store is set to true. If \a reset is true, the member data are first cleared. If the validation is successful, m_isValid is set to true, otherwise it is set to false. */ bool Formula::validate(IsotopicDataCstSPtr isotopic_data_csp, bool store, bool reset, ErrorList *error_list_p) { qsizetype error_count = error_list_p->size(); if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) { qCritical() << "Cannot validate a Formula if the isotopic data are unavailable."; error_list_p->push_back( "Cannot validate a Formula if the isotopic data are unavailable"); } // qDebug() << "isotopic_data_csp.get():" << isotopic_data_csp.get(); if(m_actionFormula.isEmpty()) { qCritical() << "Cannot validate a Formula that is empty."; error_list_p->push_back("Cannot validate a Formula that is empty"); } if(!checkSyntax()) { qCritical() << "Cannot validate a Formula that fails the syntax check test."; error_list_p->push_back( "Cannot validate a Formula that fails the syntax check test"); } // qDebug() << "Now splitting formula" << m_actionFormula << "into its action // parts."; SplitResult result = splitActionParts(isotopic_data_csp, m_plusFormula, m_minusFormula, m_symbolCountMap, 1, /*store*/ store, /*reset*/ reset); if(result == SplitResult::FAILURE) { qCritical() << "Failed splitting the Formula."; error_list_p->push_back("Failed splitting the Formula"); } // Both the action formulas cannot be empty. if(!m_plusFormula.size() && !m_minusFormula.size()) { qCritical() << "Both the plus and minus formulas are empty."; error_list_p->push_back("Both the plus and minus formulas are empty"); } // If we added errors, then that means that the Monomer was not valid. m_isValid = (error_list_p->size() > error_count ? false : true); return m_isValid; } /*! \brief Returns the status of the formula, that is, the result of validate(). */ bool Formula::isValid() const { return m_isValid; } //////////////// THE SYMBOLS-COUNT OPERATIONS ///////////////////// /*! \brief Returns a const reference to the std::map container that relates chemical symbols with corresponding counts. */ const std::map & Formula::getSymbolCountMapCstRef() const { return m_symbolCountMap; } /*! \brief Returns the count value associated with key \a symbol in the symbol / count member map m_symbolCountMap. */ double Formula::symbolCount(const QString &symbol) const { // Return the symbol index. std::map::const_iterator iter_end = m_symbolCountMap.cend(); std::map::const_iterator iter = m_symbolCountMap.find(symbol); if(iter == iter_end) return 0; return iter->second; } /*! \brief Accounts this Formula's action-formula (m_actionFormula) in the symbol / count member container (m_symbolCountMap). Calls splitActionParts() to actually parse m_actionFormula and account its components to m_symbolCountMap. The accounting of the symbol / count can be compounded by the \a times factor. While splitting the "plus" and "minus" components of the action-formula, their validity is checked against the reference isotopic data \a isotopic_data_csp. This function is used when processively accounting many different formulas into the symbol / count map. The formula is set to a new value and this function is called without resetting the symbol / count map, effectively adding formulas onto formulas sequentially. Returns true if no error was encountered, false otherwise. \sa splitActionParts(), Polymer::elementalComposition */ bool Formula::accountSymbolCounts(IsotopicDataCstSPtr isotopic_data_csp, int times) { // GCOV_EXCL_START if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) qFatal("Programming error. The isotopic data pointer cannot be nullptr."); // GCOV_EXCL_STOP // Note the 'times' param below. if(splitActionParts( isotopic_data_csp, times, /*store*/ true, /*reset*/ false) == SplitResult::FAILURE) { m_isValid = false; return false; } return true; } /*! \brief Accounts for \a symbol and corresponding \a count in the \a symbol_count_map map. The symbol_count_map relates each atom (chemical element) symbol with its occurrence count as encountered while parsing the member action-formula. If the symbol was not encountered yet, a new key/value pair is created. Otherwise, the count value is updated. Returns the new count status for \a symbol. */ double Formula::accountSymbolCountPair(std::map &symbol_count_map, const QString &symbol, double count) const { // We receive a symbol and we need to account for it count count in the member // symbol count map (count might be < 0). // Try to insert the new symbol/count pairinto the map. Check if that was done // or not. If the result.second is false, then that means the the insert // function did not perform because a pair by that symbol existed already. In // that case we just need to increment the count for the the pair. // qDebug() << "Accounting symbol:" << symbol << "for count:" << count; double new_count = 0; std::pair::iterator, bool> res = symbol_count_map.insert(std::pair(symbol, count)); if(!res.second) { // qDebug() << "The symbol was already in the symbol/count map. with // count:" // << res.first->second; // One pair by that symbol key existed already, just update the count and // store that value for reporting. res.first->second += count; new_count = res.first->second; // new_count might be <= 0. // qDebug() << "For symbol" << symbol << "the new count:" << new_count; } else { // qDebug() << "Symbol" << symbol // << "was not there already, setting the count to:" << count; // We just effectively added during the insert call above a new pair to // the map by key symbol with value count. new_count = count; } // We should check if the symbol has now a count of 0. In that case, we remove // the symbol altogether because we do not want to list naught symbols in the // final formula. if(!new_count) { // qDebug() << "For symbol" << symbol //<< "the new count is 0. Thus we erase the map item altogether."; symbol_count_map.erase(symbol); } // Update what's the text of the formula to represent what is in // atomCount list. // qDebug() << "The formula now can be reduced to:" << elementalComposition(); return new_count; } /*! \brief Accounts for \a symbol and corresponding \a count in the member map. The m_symbolCountMap relates each atom (chemical element) symbol with its occurrence count as encountered while parsing the member action-formula. If the symbol was not encountered yet, a new key/value pair is created. Otherwise, the count value is updated. Returns the new count status for \a symbol. */ double Formula::accountSymbolCountPair(const QString &symbol, double count) { return accountSymbolCountPair(m_symbolCountMap, symbol, count); } /*! \brief Accounts into this Formula the \a formula_string action-formula using \a isotopic_data_csp as reference data using \a times as a compounding factor. The result of the operation is set to \a ok. The \a formula_string formula is converted into a temporary Formula and processed: \list \li First validate() is called on the temporary formula, with storage of the symbol/count data; \li The symbol/count data thus generated is used to update the member map. \li The m_actionFormula is set to the result of elementalComposition(). \endlist Returns the size of the member symbol/count m_symbolCountMap. */ std::size_t Formula::accountFormula(const QString &formula_string, IsotopicDataCstSPtr isotopic_data_csp, double times, bool &ok) { qDebug() << "Accounting in this formula:" << m_actionFormula << "the external formula:" << formula_string; // qDebug() << "Before having merged the external formula's map into this one, // " "this one has size:" //<< m_symbolCountMap.size(); // We get a formula as an elemental composition text string and we want to // account for that formula in *this formula. // First off, validate the formula text, and take advantage to store // the resulting symbol/count pairs we'll use later to update *this Formula. Formula temp_formula(formula_string); ErrorList error_list; if(!temp_formula.validate(isotopic_data_csp, /*store*/ true, /*reset*/ true, &error_list)) { qCritical() << "Formula:" << formula_string << "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); ok = false; return 0; } // Now, for each item in the formula's symbol/count map, aggregate the found // data to *this symbol/count map. We'll have "merged" or "aggreated" the // other formula into *this one. std::map::const_iterator the_iterator_cst = temp_formula.m_symbolCountMap.cbegin(); std::map::const_iterator the_end_iterator_cst = temp_formula.m_symbolCountMap.cend(); while(the_iterator_cst != the_end_iterator_cst) { // qDebug() << "Iterating in symbol/count pair:" << iter->first << "-" << // iter->second; accountSymbolCountPair((*the_iterator_cst).first, (*the_iterator_cst).second * times); ++the_iterator_cst; } // qDebug() << "After having merged the external formula's map into this one, // " "this one has size:" //<< m_symbolCountMap.size(); // Update what's the text of the action-formula to represent what is in // atomCount list. m_actionFormula = elementalComposition(); m_title = "Has changed"; qDebug() << "And now this formula has text: " << m_actionFormula; ok = true; return m_symbolCountMap.size(); } //////////////// ELEMENTAL COMPOSITION ///////////////////// /*! \brief Returns a formula matching the contents of the memeber symbol / count map. The returned formula is formatted according to the IUPAC convention about the ordering of the chemical elements: CxxHxxNxxOxxSxxPxx. The "plus" components are output first and the "minus" components after. If \a symbol_count_pairs_p is not nullptr, each symbol / count pair is added to it. */ QString Formula::elementalComposition( std::vector> *symbol_count_pairs_p) const { // Iterate in the symbol count member map and for each item output the symbol // string accompanied by the corresponding count. Note that the count for any // given symbol might be negative. We want to craft an elemental composition // that accounts for "actions", that is a +elemental formula and a -elemental // formula. std::map::const_iterator iter = m_symbolCountMap.cbegin(); std::map::const_iterator iter_end = m_symbolCountMap.cend(); #if 0 qDebug() << "While computing the elemental composition corresponding to the " "symbol/count map:"; for(auto pair : m_symbolCountMap) qDebug().noquote() << "(" << pair.first << "," << pair.second << ")"; #endif QStringList negativeStringList; QStringList positiveStringList; while(iter != iter_end) { QString symbol = iter->first; double count = iter->second; if(count < 0) { negativeStringList.append( QString("%1%2").arg(symbol).arg(-1 * count)); } else { positiveStringList.append(QString("%1%2").arg(symbol).arg(count)); } ++iter; } // We want to provide a formula that lists the positive component // first and the negative component last. // Each positive/negative component will list the atoms in the // conventional order : CxxHxxNxxOxx and all the rest in // alphabetical order. // We want to provide for each positive and negative components of the // initial formula object, an elemental formula that complies with the // convention : first the C atom, next the H, N, O, S, P atoms and all the // subsequent ones in alphabetical order. // Sort the lists. negativeStringList.sort(); positiveStringList.sort(); // Thus we look for the four C, H, N, O, S,P atoms, and we create the // initial part of the elemental formula. Each time we find one // such atom we remove it from the list, so that we can later just // append all the remaining atoms, since we have sorted the lists // above. // The positive component // ====================== int symbol_index_in_list = 0; QString positiveComponentString; // Carbon symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("C\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("C", m_symbolCountMap.at("C"))); } // Hydrogen symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("H\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("H", m_symbolCountMap.at("H"))); } // Nitrogen symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("N\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("N", m_symbolCountMap.at("N"))); } // Oxygen symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("O\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("O", m_symbolCountMap.at("O"))); } // Sulfur symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("S\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("S", m_symbolCountMap.at("S"))); } // Phosphorus symbol_index_in_list = positiveStringList.indexOf(QRegularExpression("P\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { positiveComponentString += positiveStringList.at(symbol_index_in_list); positiveStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("P", m_symbolCountMap.at("P"))); } // Go on with all the other ones, if any... for(int iter = 0; iter < positiveStringList.size(); ++iter) { positiveComponentString += positiveStringList.at(iter); QRegularExpression regexp("([A-Z][a-z]*)(\\d*[\\.]?\\d*)"); QRegularExpressionMatch match = regexp.match(positiveStringList.at(iter)); if(match.hasMatch()) { QString symbol = match.captured(1); QString howMany = match.captured(2); bool ok = false; double count = howMany.toDouble(&ok); if(!count && !ok) qFatal( "Fatal error at %s@%d -- %s(). " "Failed to parse an atom count." "Program aborted.", __FILE__, __LINE__, __FUNCTION__); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair(symbol, count)); } } // qDebug() << __FILE__ << __LINE__ //<< "positiveComponentString:" << positiveComponentString; // The negative component // ====================== QString negativeComponentString; // Carbon symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("C\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("C", m_symbolCountMap.at("C"))); } // Hydrogen symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("H\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("H", m_symbolCountMap.at("H"))); } // Nitrogen symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("N\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("N", m_symbolCountMap.at("N"))); } // Oxygen symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("O\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("O", m_symbolCountMap.at("O"))); } // Sulfur symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("S\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("S", m_symbolCountMap.at("S"))); } // Phosphorus symbol_index_in_list = negativeStringList.indexOf(QRegularExpression("P\\d*[\\.]?\\d*")); if(symbol_index_in_list != -1) { negativeComponentString += negativeStringList.at(symbol_index_in_list); negativeStringList.removeAt(symbol_index_in_list); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair("P", m_symbolCountMap.at("P"))); } // Go on with all the other ones, if any... for(int iter = 0; iter < negativeStringList.size(); ++iter) { negativeComponentString += negativeStringList.at(iter); QRegularExpression regexp("([A-Z][a-z]*)(\\d*[\\.]?\\d*)"); QRegularExpressionMatch match = regexp.match(negativeStringList.at(iter)); if(match.hasMatch()) { QString symbol = match.captured(1); QString howMany = match.captured(2); bool ok = false; double count = howMany.toInt(&ok, 10); if(!count && !ok) qFatal( "Fatal error at %s@%d -- %s(). " "Failed to parse an atom count." "Program aborted.", __FILE__, __LINE__, __FUNCTION__); if(symbol_count_pairs_p) symbol_count_pairs_p->push_back( std::pair(symbol, count)); } } // qDebug() << __FILE__ << __LINE__ //<< "negativeComponentString:" << negativeComponentString; // Create the final elemental formula that comprises both the // positive and negative element. First the positive element and // then the negative one. Only append the negative one, prepended // with '-' if the string is non-empty. QString elementalComposition = positiveComponentString; if(!negativeComponentString.isEmpty()) elementalComposition += QString("-%1").arg(negativeComponentString); // qDebug() << __FILE__ << __LINE__ // <<"elementalComposition:" << elementalComposition; return elementalComposition; } //////////////// MASS OPERATIONS ///////////////////// /*! \brief Accounts this formula's monoisotopic and average masses into \a mono and \a avg, using \a times as a compounding factor. The masses corresponding to the member action-formula are calculated first and then the \a mono and \a avg parameters are updated by incrementing their value with the calculated values. This incrementation might be compounded by that \a times factor. The masses of the member action-formula are computed using data from \a isotopic_data_csp. Sets \a ok to false if the calculation failed, to true otherwise. Returns this object. \sa splitActionParts() */ Formula & Formula::accountMasses(bool &ok, IsotopicDataCstSPtr isotopic_data_csp, double &mono, double &avg, double times) { // GCOV_EXCL_START if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) qFatal("Programming error. The isotopic data pointer cannot be nullptr."); // GCOV_EXCL_STOP // Note the 'times' param below that ensures we create proper symbol/count // map items by taking that compounding factor into account. // qDebug() << qSetRealNumberPrecision(6) // << "We get two mono and avg variables with values:" << mono << // "-" // << avg << "and times:" << times; if(!checkSyntax()) { qDebug() << "The checkSyntax test failed for " << m_actionFormula; m_isValid = false; ok = false; return *this; } if(splitActionParts( isotopic_data_csp, times, true /* store */, true /* reset */) == SplitResult::FAILURE) { qDebug() << "The formula splitting into actions failed."; m_isValid = false; ok = false; return *this; } // qDebug() << "after splitActionParts:" // << "store: true ; reset: true" // << "m_actionFormula:" << m_actionFormula << "text" << toString(); // At this point m_symbolCountMap has all the symbol/count pairs needed to // account for the masses. std::map::const_iterator iter = m_symbolCountMap.cbegin(); std::map::const_iterator iter_end = m_symbolCountMap.cend(); // for(auto item : m_symbolCountMap) // qDebug() << "One symbol count item:" << item.first << "/" << item.second; bool res = false; while(iter != iter_end) { QString symbol = iter->first; // qDebug() << "Getting masses for symbol:" << symbol; double mono_mass = isotopic_data_csp->getMonoMassBySymbol(iter->first, res); if(!res) { qWarning() << "Failed to get the mono mass."; ok = false; return *this; } mono += mono_mass * iter->second; ok = false; double avg_mass = isotopic_data_csp->getAvgMassBySymbol(iter->first, res); if(!res) { qWarning() << "Failed to get the avg mass."; ok = false; return *this; } avg += avg_mass * iter->second; ++iter; } ok = true; m_isValid = true; return *this; } /*! \brief Accounts the \a formula monoisotopic and average masses into \a mono and \a avg, using \a times as a compounding factor. The masses corresponding to the \a formula are calculated first and then the \a mono and \a avg parameters are updated by incrementing their value with the calculated values. This incrementation might be compounded by that \a times factor. The masses of the \a formula are computed using data from \a isotopic_data_csp. Sets ok to false if the calculation failed, to true otherwise. Returns this object. \sa splitActionParts() */ Formula & Formula::accountMasses(Formula &formula, bool &ok, IsotopicDataCstSPtr isotopic_data_csp, double &mono, double &avg, double times) { // GCOV_EXCL_START if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr || !isotopic_data_csp->size()) qFatal("Programming error. The isotopic data pointer cannot be nullptr."); // GCOV_EXCL_STOP // Note the 'times' param below that ensures we create proper symbol/count // map items by taking that compounding factor into account. // qDebug() << qSetRealNumberPrecision(6) // << "We get two mono and avg variables with values:" << mono << // "-" // << avg << "and times:" << times; if(!formula.checkSyntax()) { qDebug() << "The checkSyntax test failed for " << formula.m_actionFormula; formula.m_isValid = false; ok = false; return formula; } if(formula.splitActionParts(isotopic_data_csp, formula.m_plusFormula, formula.m_minusFormula, formula.m_symbolCountMap, times, true /* store */, true /* reset */) == SplitResult::FAILURE) { formula.m_isValid = false; ok = false; return formula; } // qDebug() << "after splitActionParts:" // << "store: true ; reset: true" // << "m_actionFormula:" << m_actionFormula << "text" << toString(); // At this point m_symbolCountMap has all the symbol/count pairs needed to // account for the masses. std::map::const_iterator iter = formula.m_symbolCountMap.cbegin(); std::map::const_iterator iter_end = formula.m_symbolCountMap.cend(); // for(auto item : m_symbolCountMap) // qDebug() << "One symbol count item:" << item.first << "/" << item.second; bool res = false; while(iter != iter_end) { QString symbol = iter->first; // qDebug() << "Getting masses for symbol:" << symbol; double mono_mass = isotopic_data_csp->getMonoMassBySymbol(iter->first, res); if(!res) { qWarning() << "Failed to get the mono mass."; ok = false; return formula; } mono += mono_mass * iter->second; ok = false; double avg_mass = isotopic_data_csp->getAvgMassBySymbol(iter->first, res); if(!res) { qWarning() << "Failed to get the avg mass."; ok = false; return formula; } avg += avg_mass * iter->second; ++iter; } ok = true; formula.m_isValid = true; return formula; } //////////////// XML DATA LOADING WRITING ///////////////////// /*! \brief Parses a formula XML \a element according to \a version and sets the data to the member action-formula checking it syntax. Returns true if parsing and syntax checking were successful, false otherwise. \sa checkSyntax() */ bool Formula::renderXmlFormulaElement(const QDomElement &element, [[maybe_unused]] int version) { if(element.tagName() != "formula") { qCritical() << "The element tag is not 'formula'"; return false; } // Will take care of removing the title and setting it to m_title. setActionFormula(element.text()); // qDebug() << "Now set the action-formula to " << m_actionFormula; // Do not forget that we might have a title associated with the // formula and spaces. checkSyntax() should care of removing these // title and spaces before checking for chemical syntax // correctness. // Remember, syntax checking does not mean that the chemical // validity is assessed. For example "+HWKPTR" would pass // the checkSyntax() test. if(!checkSyntax()) { qCritical() << "Failed to check syntax for formula:" << m_actionFormula; return false; } return true; } /*! \brief Returns a string containing a formula XML element documenting this Formula instance. \a offset and \a indent define the formatting of the XML element. */ QString Formula::formatXmlFormulaElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString string; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } string += QString("%1%2\n") .arg(lead) .arg(getActionFormula(/*with title*/ true)); return string; } //////////////// UTILS ///////////////////// /*! \brief Removes \e{all} the space characters from the member action-formula. Spaces can be placed anywhere in formula for more readability. However, it might be required that these character spaces be removed. This function does just this, using a QRegularExpression. Returns the number of removed characters. */ int Formula::removeSpaces() { int length = m_actionFormula.length(); // We want to remove all the possibly-existing spaces. m_actionFormula.remove(QRegularExpression("\\s+")); // Return the number of removed characters. return (length - m_actionFormula.length()); } /*! \brief Returns the total count of symbols (atoms) in this formula. The determination is performed by summing up all the count values for all the symbols in the member symbol / count pairs in the member map m_symbolCountMap. */ double Formula::totalAtoms() const { double total_atom_count = 0; std::map::const_iterator iter = m_symbolCountMap.cbegin(); std::map::const_iterator iter_end = m_symbolCountMap.cend(); while(iter != iter_end) { total_atom_count += iter->second; ++iter; } return total_atom_count; } /*! \brief Returns the total count of isotopes in this formula using \a isotopic_data_csp as the reference isotopic data. The determination is performed by summing up all the isotope counts for all the symbols keys in the member symbol / count map m_symbolCountMap. */ double Formula::totalIsotopes(IsotopicDataCstSPtr isotopic_data_csp) const { double total_isotope_count = 0; std::map::const_iterator iter = m_symbolCountMap.cbegin(); std::map::const_iterator iter_end = m_symbolCountMap.cend(); while(iter != iter_end) { total_isotope_count += iter->second * isotopic_data_csp->getIsotopeCountBySymbol(iter->first); ++iter; } return total_isotope_count; } /*! \brief Clears \e{all} the formula member data. */ void Formula::clear() { m_title.clear(); m_actionFormula.clear(); m_plusFormula.clear(); m_minusFormula.clear(); m_forceCountIndex = false; m_symbolCountMap.clear(); m_isValid = false; } void Formula::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&Formula::staticMetaObject); engine->globalObject().setProperty("Formula", jsMetaObject); } //////////////// PRIVATE FUNCTIONS ///////////////////// //////////////// PRIVATE FUNCTIONS ///////////////////// //////////////// PRIVATE FUNCTIONS ///////////////////// // GCOV_EXCL_START /*! \brief Sets the m_plusFormula formula to \a formula. */ void Formula::setPlusFormula(const QString &formula) { m_plusFormula = formula; } /*! \brief Sets the m_minusFormula formula to \a formula. */ void Formula::setMinusFormula(const QString &formula) { m_minusFormula = formula; } // GCOV_EXCL_STOP MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Formula) } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/FragmentationConfig.cpp000664 001750 001750 00000030353 15100504560 026717 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/FragmentationConfig.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::FragmentationConfig \inmodule libXpertMassCore \ingroup PolChemDefGasPhaseChemicalReactions \inheaderfile FragmentationConfig.hpp \brief The FragmentationConfig class derives from FragmentationPathway and adds functionality to configure the way the fragmentation occurs in the \l{Oligomer} \l{Sequence}. Since an Oligomer is actually defined to merely be a monomer range in a given \l{Polymer} \l{Sequence}, the configuration handles the definition of the Polymer sequence region that needs to undergo fragmentation. Because the user might need to apply specific gas phase chemical reactions upon fragmentation of the Oligomer that are not defined per se in the FragmentationPathway, a container of formulas enables the application of any number of such chemical reactions for each fragmentation event. This is useful in any polymer chemistry definition, because most often the Oligomer undergoing fragmentation has loss of neutral ions decompositions like \e{NH\sub{3}} or \e{H\sub{2}O}. The user might need to generate fragmentation ions that are of different ionization levels. This class provides settings for this eventuality. \sa FragmentationPathway */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_startIndex \brief The index that delimits the start position (index) of the Polymer Sequence that defines the Oligomer undergoing fragmentation. */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_stopIndex \brief The index that delimits the stop position (index) of the Polymer Sequence that defines the Oligomer undergoing fragmentation. */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_startIonizeLevel \brief The first value of the ionization level range. */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_stopIonizeLevel \brief The last value of the ionization level range. */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_sequenceEmbedded \brief Specifies if the sequence of the fragments needs to be stored or not in the Oligomer fragments. */ /*! \variable MsXpS::libXpertMassCore::FragmentationConfig::m_formulas \brief Container for Formula instances that describe additional decomposition reactions like those often observed in protein gas phase chemistry (loss of ammonia and/or of water). */ /*! \brief Constructs a fragmentation configuration with a number of parameters. \list \li \a pol_chem_def_csp Polymer chemistry definition that is passed to the FragmentationPathway base class. Cannot be nullptr (fatal error if so). \li \a name Name that is passed to the FragmentationPathway base class. Cannot be empty. \li \a formula Formula that is passed to the FragmentationPathway base class. \li \a frag_end The end of the Oligomer undergoing fragmentation that is found in the product ions. Used to initialize the FragmentationPathway base class. \li \a comment Comment that is passed to the FragmentationPathway base class. Defaults to the null string. \li \a sequence_embedded Indicates if the product ion sequence needs to be stored in the fragment Oligomer. \endlist \sa FragmentationPathway */ FragmentationConfig::FragmentationConfig(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &formula, Enums::FragEnd frag_end, const QString &comment, bool sequence_embedded) : FragmentationPathway(pol_chem_def_csp, name, formula, frag_end, comment), m_sequenceEmbedded(sequence_embedded) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr;"; } /*! \brief Constructs a fragmentation configuration with a number of parameters. \list \li \a fragmentation_pathway Fragmentation pathway that will become the base class instance of this instance. \li \a start_index Start position (index) in the Polymer Sequence that delimits the Oligomer undergoing fragmentation. \li \a stop_index Stop position (index) in the Polymer Sequence that delimits the Oligomer undergoing fragmentation. \li \a sequence_embedded Indicates if the product ion sequence needs to be stored in the fragment Oligomer. \endlist The PolChemDef member of the FragmentationPathway base class is tested. If it is nullptr, that is a fatal error. \sa FragmentationPathway */ FragmentationConfig::FragmentationConfig( const FragmentationPathway &fragmentation_pathway, int start_index, int stop_index, bool sequence_embedded) : FragmentationPathway(fragmentation_pathway), m_startIndex(start_index), m_stopIndex(stop_index), m_sequenceEmbedded(sequence_embedded) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr;"; } /*! \brief Constructs a fragmentation configuration as a copy of \a other. The PolChemDef member of the FragmentationPathway base class is tested. If it is nullptr, that is a fatal error. \sa FragmentationPathway */ FragmentationConfig::FragmentationConfig(const FragmentationConfig &other) : FragmentationPathway(other), m_startIndex(other.m_startIndex), m_stopIndex(other.m_stopIndex), m_startIonizeLevel(other.m_startIonizeLevel), m_stopIonizeLevel(other.m_stopIonizeLevel), m_sequenceEmbedded(other.m_sequenceEmbedded), m_formulas(other.m_formulas) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr;"; } /*! \brief Constructs this fragmentation configuration. */ FragmentationConfig::~FragmentationConfig() { m_formulas.clear(); } /*! \brief Assigns \a other to this FragmentationConfig instance. */ FragmentationConfig & FragmentationConfig::operator=(const FragmentationConfig &other) { if(&other == this) return *this; FragmentationPathway::operator=(other); m_startIndex = other.m_startIndex; m_stopIndex = other.m_stopIndex; m_startIonizeLevel = other.m_startIonizeLevel; m_stopIonizeLevel = other.m_stopIonizeLevel; m_sequenceEmbedded = other.m_sequenceEmbedded; for(const Formula &formula : other.m_formulas) m_formulas.push_back(formula); return *this; } /*! \brief Sets the Oligomer start \a index in the Polymer Sequence. */ void FragmentationConfig::setStartIndex(std::size_t index) { m_startIndex = index; } /*! \brief Returns the Oligomer start index in the Polymer Sequence. */ std::size_t FragmentationConfig::getStartIndex() const { return m_startIndex; } /*! \brief Sets the Oligomer stop \a index in the Polymer Sequence. */ void FragmentationConfig::setStopIndex(std::size_t index) { m_stopIndex = index; } /*! \brief Returns the Oligomer stop index in the Polymer Sequence. */ std::size_t FragmentationConfig::getStopIndex() const { return m_stopIndex; } /*! \brief Sets the start \a value of the ionization range. */ void FragmentationConfig::setStartIonizeLevel(std::size_t value) { if(value <= m_stopIonizeLevel) { m_startIonizeLevel = value; } else { m_startIonizeLevel = m_stopIonizeLevel; m_stopIonizeLevel = value; } } /*! \brief Returns the start value of the ionization range. */ std::size_t FragmentationConfig::getStartIonizeLevel() const { return m_startIonizeLevel; } /*! \brief Sets the stop \a value of the ionization range. */ void FragmentationConfig::setStopIonizeLevel(std::size_t value) { if(value > m_startIonizeLevel) { m_stopIonizeLevel = value; } else { m_startIonizeLevel = m_stopIonizeLevel; m_stopIonizeLevel = value; } } /*! \brief Returns the stop value of the ionization range. */ std::size_t FragmentationConfig::getStopIonizeLevel() const { return m_stopIonizeLevel; } /*! \brief Sets the \a start and \a stop values of the ionization range. */ void FragmentationConfig::setIonizeLevels(std::size_t start, std::size_t stop) { m_startIonizeLevel = start; m_stopIonizeLevel = stop; } /*! \brief Adds a \a formula to the container of Formula instances. Returns true if the formula validated successfully, false otherwise. */ bool FragmentationConfig::addFormula(const Formula &formula) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr;"; IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); ErrorList error_list; if(!formula.validate(isotopic_data_csp, &error_list)) { qCritical() << "Formula" << formula.getActionFormula() << "failed to validate with errors:" << Utils::joinErrorList(error_list); return false; } // Check that the formula is not already in the list. std::vector::const_iterator the_iterator_cst = std::find_if(m_formulas.cbegin(), m_formulas.cend(), [&formula](const Formula &iter_formula) { return iter_formula == formula; }); if(the_iterator_cst != m_formulas.cend()) return false; // At this point we can say that the formula is OK and can be // added. m_formulas.push_back(formula); return true; } /*! \brief Adds a Formula to the container of Formula instances using \a formula_string. */ bool FragmentationConfig::addFormula(const QString &formula_string) { // With the string make a true Formula instance. Formula formula = Formula(formula_string); return addFormula(formula); } /*! \brief Returns a const reference to the container of Formula instances. */ const std::vector & FragmentationConfig::getFormulasCstRef() const { return m_formulas; } /*! \brief Returns a reference to the container of Formula instances. */ std::vector & FragmentationConfig::getFormulasRef() { return m_formulas; } /*! \brief Sets if the product ion Oligomer's sequence needs to be stored to \a value. */ void FragmentationConfig::setSequenceEmbedded(bool value) { m_sequenceEmbedded = value; } /*! \brief Returns if the product ion Oligomer's sequence needs to be stored. */ bool FragmentationConfig::isSequenceEmbedded() const { return m_sequenceEmbedded; } /*! \brief Returns a string describing this FragmentationConfig instance. */ QString FragmentationConfig::toString() const { QString text = "FragmentationConfig: \n"; text += FragmentationPathway::toString(); text += "\n"; text += QString("Indices [%1-%2] -- ionization levels [%3-%4].\n") .arg(m_startIndex) .arg(m_stopIndex) .arg(m_startIonizeLevel) .arg(m_stopIonizeLevel); if(m_formulas.size()) { text += "Formulas:\n"; for(const Formula &formula : m_formulas) text += QString("%1 - \n").arg(formula.getActionFormula()); } return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/FragmentationPathway.cpp000664 001750 001750 00000117624 15100504560 027136 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/FragmentationPathway.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::FragmentationPathway \inmodule libXpertMassCore \ingroup PolChemDefGasPhaseChemicalReactions \inheaderfile FragmentationPathway.hpp \brief The FragmentationPathway class provides a model for specifying gas phase fragmentation pathways of \l{Oligomer} \l{Sequence}s. The FragmentationPathway class provides the description of a fragmentation pathway. Fragmentation pathways determine the chemical reaction that governs the fragmentation of the polymer in the gas-phase. The chemical reaction is embodied by a formula. The side of the polymer (left or right) that makes the fragment after the fragmentation has occurred is described by a fragmentation-end enumeration. A fragmentation specification might not be enough information to determine the manner in which a polymer fragments in the gas-phase. Fragmentation rules (\l{FragmentationRule}s) might be required to refine the specification. A fragmentation specification might hold as many \l{FragmentationRule}s as required. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::mcsp_polChemDef \brief The PolChemDef (polymer chemistry definition) that is the context in which the Oligomer being fragmented exists. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_name \brief The name of the FragmentationPathway. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_formula \brief A \l{Formula} instance describing the fragmentation reaction occurring on the Monomer at which the decomposition occurs. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_fragEnd \brief The end of the Oligomer precursor ion that is found in the product ion. This member datum defines the end of the Oligomer sequence being fragmented that is kept in the product ions (a, b, c ions keep the left end of the Oligomer, while x, y, z ions keep the right end of the Oligomer; instead in protein ammonium ions, no end is found in the product ions). */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_monomerContribution \brief Contribution of the Monomer skeleton when decomposition occurs. In some situations (nucleic acids, for example), upon fragmentation, the Oligomer looses the nucleic base at the location of the backbone decomposition (which yields an abasic ion product). This member allows to indicate that the monomer (that is, the residue) is to be accounted for (by a -1 value, the program accounts that the monomer is lost during decomposition). But removing the whole monomer is not correct because that removes too much material, so the member formula should account for the compensation of the removed backbone. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_comment \brief A comment associated to the FragmentationPathway. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_rules \brief The container of FragmentationRuleSPtr that allow refining how the fragmentation occurs at site depending on the identity of the Monomer occurring either at previous position or next position with respect to the position in the Oligomer undergoing fragmentation. The FragmentationRule instances are required in the gas phase chemistry of sugars where the way decomposition occurs at a given site depends on the identity of the immediate proximity of the decomposing backbone region. */ /*! \variable MsXpS::libXpertMassCore::FragmentationPathway::m_isValid \brief The validity status of this FragmentationPathway instance. */ /*! \brief Constructs a FragmentationPathway instance starting from an XML \a element according to \a version and using a reference \a pol_chem_def_csp polymer chemistry definition. This is the current format (FAKE fgr): \code c LE +N1H2 0 thefragmentationpathwaycomment a-fgr-2 +H100 F D E therulecomment \endcode The rendering of the FragmentationPathway instances requires that the PolChemDef be available. Depending on \a version, two different functions are used to actually render the XML element. Before version 2, the XML element was named (FragSpec class) and starting with version 2, the XML is named (FragmentationPathway class). \sa renderXmlFgpElement(), renderXmlFgsElement() */ FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version) : mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing FragmentationPathway with no PolChemDef."; if(version == 1) { if(!renderXmlFgsElement(element, version)) qCritical() << "Failed to fully render or validate the FragSpec XML element " "for construction of FragSpec instance."; } else if(version == 2) { if(!renderXmlFgpElement(element, version)) qCritical() << "Failed to fully render or validate the " "FragmentationPathway XML element " "for construction of FragmentationPathway instance."; } else qFatalStream() << "Programming error. The polymer chemistry definition version " "is not correct."; } /*! \brief Constructs a FragmentationPathway instance with a number of parameters. \list \li \a pol_chem_def_csp The polymer chemistry definition (PolChemDef) \li \a name The name of the fragmentation pathway (like 'y' for protein gas phase chemistry) \li \a formula_string The formula that describes the gas phase reaction \li \a frag_end The end of the Oligomer sequence being fragmented that is kept in the product ions (a, b, c ions keep the left end of the Oligomer, while x, y, z ions keep the right end of the Oligomer) \li \a comment A comment associated to the fragmentation pathway ("Observed with high energy collisions", for example) \endlist Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &formula_string, Enums::FragEnd frag_end, const QString &comment) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_formula(Formula(formula_string)), m_fragEnd(frag_end), m_comment(comment) { qDebug() << "Constructing FragmentationPathway with name:" << m_name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of the FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a FragmentationPathway instance as a copy of \a other. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ FragmentationPathway::FragmentationPathway(const FragmentationPathway &other) : mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_formula(other.m_formula), m_fragEnd(other.m_fragEnd), m_monomerContribution(other.m_monomerContribution), m_comment(other.m_comment) { m_rules = other.m_rules; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon copy-construction of the FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destroys this FragmentationPathway instance. */ FragmentationPathway::~FragmentationPathway() { m_rules.clear(); } //////////////// THE POLCHEMDEF ///////////////////// /*! \brief Sets the PolChemDef (polymer chemistry definition) to \a pol_chem_def_csp. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ void FragmentationPathway::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting PolChemDef of FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the PolChemDef (polymer chemistry definition). */ PolChemDefCstSPtr FragmentationPathway::getPolchemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the name to \a name. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ void FragmentationPathway::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting name of FragmentationPathway, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ const QString & FragmentationPathway::getName() const { return m_name; } //////////////// THE FORMULA ///////////////////// /*! \brief Sets the member Formula to \a formula. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ void FragmentationPathway::setFormula(const Formula &formula) { m_formula = formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting Formula of FragmentationPathway, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Sets the member Formula using \a formula_string. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ void FragmentationPathway::setFormula(const QString &formula_string) { m_formula = Formula(formula_string); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting Formula of FragmentationPathway, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns a const reference to the member \l Formula. */ const Formula & FragmentationPathway::getFormulaCstRef() const { return m_formula; } /*! \brief Returns a reference to the member \l Formula. */ Formula & FragmentationPathway::getFormulaRef() { return m_formula; } //////////////// THE FRAG END ///////////////////// /*! \brief Sets the precursor end in the fragment to \a frag_end. Upon fragmentation, two fragments are generated, one holding the left end of the initial precursor ion and one holding the right end. \a frag_end indicates if the fragment is a left end fragment or a right end fragment. For example, in protein gas phase chemistry, the ions of the series a, b and c hold the left end of the precursor ion, while the ions of the series x, y, and z hold the right end of the precursor ion. */ void FragmentationPathway::setFragEnd(Enums::FragEnd frag_end) { m_fragEnd = frag_end; } /*! \brief Returns the \l Enums::FragEnd. */ Enums::FragEnd FragmentationPathway::getFragEnd() const { return m_fragEnd; } //////////////// THE MONOMER CONTRIBUTION ///////////////////// /*! \brief Sets the Monomer contribution to \a monomer_contribution. In certain fragmentation pathways, the monomer undergoing decomposition might loose a portion of its structure. This is the case in nucleic acids fragmentation, when the nucleic base might be detached from the monomer undergoing the fragmentation reaction. This member allows to indicate if a part of the monomer is detaching upon decomposition. In this case, this value is negative (-1) and indicates that the mass of the monomer is to be removed from the mass of the fragment. However, because it is not the full monomer that decomposes away, but only a part of it, then it is the responsibility of the polymer chemistry definition designer to add back the formula of the conserved monomer structure. For example, in DNA fragmentation, the \c{abasic a} fragmentation pathway is defined with a monomer contribution of -1, with the \c{-HOH+C5H8O5P} formula to account for the remaining structure of the monomer (this combination has a net loss of the base only and does not account for loss of the phospho moiety of the monomer). */ void FragmentationPathway::setMonomerContribution(int monomer_contribution) { m_monomerContribution = monomer_contribution; } /*! \brief Returns the Monomer contribution. */ int FragmentationPathway::getMonomerContribution() const { return m_monomerContribution; } //////////////// THE COMMENT ///////////////////// /*! \brief Sets the comment to \a comment. */ void FragmentationPathway::setComment(const QString &comment) { m_comment = comment; } /*! \brief Returns the comment. */ QString FragmentationPathway::getComment() const { return m_comment; } //////////////// THE RULES CONTAINER ///////////////////// /*! \brief Returns a const reference to the container of \l FragmentationRule instances. */ const std::vector & FragmentationPathway::getRulesCstRef() const { return m_rules; } /*! \brief Returns a reference to the container of \l FragmentationRule instances. */ std::vector & FragmentationPathway::getRulesRef() { return m_rules; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this FragmentationPathway instance. Returns a reference to this FragmentationPathway. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. \sa validate() */ FragmentationPathway & FragmentationPathway::operator=(const FragmentationPathway &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_formula = other.m_formula; m_fragEnd = other.m_fragEnd; m_monomerContribution = other.m_monomerContribution; m_comment = other.m_comment; m_rules = other.m_rules; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment-configuration of the FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if \c this and \a other are identical. The member PolChemDef is compared deeply and the member rules also. Because the FragmentationRule instances are not a reference to the PolChemDef, their comparison is deep. */ bool FragmentationPathway::operator==(const FragmentationPathway &other) const { if(&other == this) return true; // We cannot do this, because each chemical entity that references the // polymer chemistry definition will call this function and an // infinite loop ensues. // if(!mcsp_polChemDef->isChemicallyEquivalent(*other.mcsp_polChemDef)) // return false; if(!(m_name == other.m_name && m_formula == other.m_formula && m_fragEnd == other.m_fragEnd && m_monomerContribution == other.m_monomerContribution && m_comment == other.m_comment && m_rules.size() == other.m_rules.size())) return false; for(std::size_t iter = 0; iter < m_rules.size(); ++iter) if((*m_rules.at(iter)) != (*other.m_rules.at(iter))) return false; return true; } /*! \brief Returns true if \c this and \a other are different. Returns the negated result of operator==(). */ bool FragmentationPathway::operator!=(const FragmentationPathway &other) const { if(&other == this) return false; return !operator==(other); } //////////////// RULES ///////////////////// /*! \brief Adds the \a frag_rule_sp FragmentationRule instance to the member container of FragmentationRule instances. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. */ void FragmentationPathway::addRule(FragmentationRuleSPtr frag_rule_sp) { if(frag_rule_sp == nullptr || frag_rule_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; m_rules.push_back(frag_rule_sp); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment-configuration of the FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Inserts in the container of FragmentationRule instances at \a index the \a frag_rule_sp FragmentationRule instance. If index is the container size, the FragmentationRule is added at the end of the container. Validation of this instance occurs after member data initialization and the result of the validation process is set to member datum m_isValid. */ void FragmentationPathway::insertRuleAt(FragmentationRuleSPtr frag_rule_sp, std::size_t index) { if(frag_rule_sp == nullptr || frag_rule_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; if(index > m_rules.size()) qFatalStream() << "Programming error. Index out of bounds."; if(index == m_rules.size()) addRule(frag_rule_sp); m_rules.insert(m_rules.cbegin() + index, frag_rule_sp); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment-configuration of the FragmentationPathway, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Removes from the container of FragmentationRule instances the item at index \a index. */ void FragmentationPathway::removeRuleAt(size_t index) { if(index >= m_rules.size()) qFatalStream() << "Programming error. Index is out of bounds."; m_rules.erase(m_rules.begin() + index); } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns the FragmentationPathway instance from the polymer chemistry definition registered in this instance. The key to search the FragmentationPathway is this instance's name member. If there is no PolChemDef available, nullptr is returned. If no FragmentationPathway instance is found by this instance's name, nullptr is returned. */ FragmentationPathwayCstSPtr FragmentationPathway::getFromPolChemDefByName() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_name.isEmpty()) return nullptr; return mcsp_polChemDef->getFragmentationPathwayCstSPtrByName(m_name); } /*! \brief Returns the status of this FragmentationPathway instance the polymer chemistry definition registered in this instance. The key to search the FragmentationPathway is this instance's name member. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no FragmentationPathway instance is found by this instance's name, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus FragmentationPathway::isKnownByNameInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getFragmentationPathwayCstSPtrByName(m_name) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; } /*! \brief Validates the FragmentationPathway, recording any error with a message in \a error_list_p. The validation involves checking that: \list \li The polymer chemistry definition is available (non-nullptr) \li The name is not empty \li The formula is not empty and validates successfully \li Any defined fragmentation rule validates successfully \endlist If the validation is successful, m_isValid is set to true, otherwise it is set to false. Returns the outcome of the validation. */ bool FragmentationPathway::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "A FragmentationPathway with no PolChemDef available cannot " "validate successfully."; error_list_p->push_back( "A FragmentationPathway with no PolChemDef available cannot validate " "successfully"); } if(m_name.isEmpty()) { qCritical() << "A FragmentationPathway with no name cannot validate successfully."; error_list_p->push_back( "A FragmentationPathway with no name cannot validate successfully"); } if(m_formula.getActionFormula().isEmpty()) { qCritical() << "A FragmentationPathway with no formula cannot " "validate successfully."; error_list_p->push_back( "A FragmentationPathway with no formula cannot validate " "successfully"); } else if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "A FragmentationPathway with an invalid formula cannot " "validate successfully."; error_list_p->push_back( "A FragmentationPathway with an invalid formula cannot validate " "successfully"); } // Do not test contribution, that might be any value. // Comment is optional // FragmentationRule instances are optional, but if there, they should // validate successfully. for(const FragmentationRuleSPtr &frag_rule_sp : m_rules) { if(!frag_rule_sp->validate(error_list_p)) { qCritical() << "A FragmentationPathway with an invalid fragmentation " "rule cannot " "validate successfully."; error_list_p->push_back( "A FragmentationPathway with an invalid fragmentation rule cannot " "validate " "successfully"); } } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this FragmentationPathway. */ bool FragmentationPathway::isValid() const { return m_isValid; } //////////////// UTILITIES ///////////////////// /*! \brief Returns a string with the textual representation of this FragmentationPathway instance. */ QString FragmentationPathway::toString() const { QString text = "Fragmentation pathway:\n"; QString frag_end_string = (*(fragEndMap.find(m_fragEnd))).second; text += QString("Name: %1 - Formula: %2 - Frag end: %3 - Monomer contribution: %4") .arg(m_name) .arg(m_formula.getActionFormula(/*with_title*/ true)) .arg(frag_end_string) .arg(m_monomerContribution); if(m_rules.size()) { text += " - Rules:\n"; for(const FragmentationRuleCstSPtr frag_rule_sp : m_rules) { text += frag_rule_sp->toString(); } text += "\n"; } else text += "\n"; text += QString("isValid: %1\n").arg(m_isValid ? "true" : "false"); return text; } //////////////// XML DATA LOADING WRITING ///////////////////// /*! \brief Parses a fragmentation specification XML \a element using a \a{version}ed function. This function is used for polymer chemistry definition documents of version less than 2 (the XML element tag is , for FragSpec class). Upon parsing and validation of the parsed data, the member data are updated, thus essentially initializing this FragmentationPathway instance. Validation of this instance occurs after member data initialization. Returns true if parsing and formula validation were successful, false otherwise. \sa renderXmlFgpElement(), validate() */ bool FragmentationPathway::renderXmlFgsElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; QDomElement rule_child_element; bool comment_was_parsed = false; /* The xml node we are in is structured this way: * a LE -C1O1 0 opt_comment a-fgr-1 +H200 E D F opt_comment a-fgr-2 +H100 F D E opt_comment * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "fgs" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. * * The DTD says this: * */ if(element.tagName() != "fgs") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The FragSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); // qDebug() << "The name:" << m_name; child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "end") { qCritical() << "The FragSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } if(child.text() == "NE") m_fragEnd = Enums::FragEnd::NE; else if(child.text() == "LE") m_fragEnd = Enums::FragEnd::LE; else if(child.text() == "RE") m_fragEnd = Enums::FragEnd::RE; else if(child.text() == "BE") m_fragEnd = Enums::FragEnd::BE; else { qCritical() << "The FragSpec did not render correctly: problem with the " " element's text value."; m_isValid = false; return m_isValid; } child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "formula") { qCritical() << "The FragSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } if(!m_formula.renderXmlFormulaElement(child)) { qCritical() << "The FragSpec did not render correctly: the formula did not " "render correctly."; m_isValid = false; return m_isValid; } // The next element must be child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "sidechaincontrib") { qCritical() << "The FragSpec did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } QString text = child.text(); bool ok = false; m_monomerContribution = text.toInt(&ok); if(!m_monomerContribution && !ok) { qCritical() << "The FragSpec did not render correctly: problem with the " " element's value."; m_isValid = false; return m_isValid; } // The next element might be either comment or(none, one or more) // fgr. child = child.nextSiblingElement(); while(!child.isNull()) { // Is it a comment or the first of one|more elements ? // Remember: if(child.tagName() == "comment") { if(comment_was_parsed) { qCritical() << "The FragSpec did not render correctly: problem with " "multiple elements."; m_isValid = false; return m_isValid; } m_comment = child.text(); comment_was_parsed = true; child = child.nextSiblingElement(); continue; } // At this point, yes, if there is still a sibling, then it // has to be one , either alone or the first of multiple. while(!child.isNull()) { FragmentationRuleSPtr frag_rule_sp = std::make_shared(mcsp_polChemDef, child, 1); if(!frag_rule_sp->isValid()) { qCritical() << "The FragSpec did not render correctly: problem with " "a FragRule element."; frag_rule_sp.reset(); m_isValid = false; return m_isValid; } m_rules.push_back(frag_rule_sp); child = child.nextSiblingElement(); } } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate FragSpec instance right after " "rendering it from XML element, with errors:" << Utils::joinErrorList(error_list, ", "); return m_isValid; } /*! \brief Parses a fragmentation pathway XML \a element using a \a{version}ed function. This function is used for polymer chemistry definition documents of version greater or equal to 2 (the XML element tag is , for FragmentationPathway class). Upon parsing and validation of the parsed data, the member data are updated, thus essentially initializing this FragmentationPathway instance. Validation of this instance occurs after member data initialization. Returns true if parsing and formula validation were successful, false otherwise. \sa renderXmlFgsElement(), validate() */ bool FragmentationPathway::renderXmlFgpElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; QDomElement rule_child_element; bool comment_was_parsed = false; /* The xml node we are in is structured this way: * a LE -C1O1 0 opt_comment a-fgr-1 +H200 E D F opt_comment a-fgr-2 +H100 F D E opt_comment * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "fgs" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. * * The DTD says this: * */ if(element.tagName() != "fgp") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); // qDebug() << "The name:" << m_name; child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "end") { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element."; m_isValid = false; return m_isValid; } if(child.text() == "NE") m_fragEnd = Enums::FragEnd::NE; else if(child.text() == "LE") m_fragEnd = Enums::FragEnd::LE; else if(child.text() == "RE") m_fragEnd = Enums::FragEnd::RE; else if(child.text() == "BE") m_fragEnd = Enums::FragEnd::BE; else { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element's text value."; m_isValid = false; return m_isValid; } child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "formula") { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element."; m_isValid = false; return m_isValid; } if(!m_formula.renderXmlFormulaElement(child)) { qCritical() << "The FragmentationPathway did not render correctly: the " "formula did not " "render correctly."; m_isValid = false; return m_isValid; } // The next element must be child = child.nextSiblingElement(); if(child.isNull() || child.text().isEmpty() || child.tagName() != "sidechaincontrib") { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element."; m_isValid = false; return m_isValid; } QString text = child.text(); bool ok = false; m_monomerContribution = text.toInt(&ok); if(!m_monomerContribution && !ok) { qCritical() << "The FragmentationPathway did not render correctly: " "problem with the " " element's value."; m_isValid = false; return m_isValid; } // The next element might be either comment or(none, one or more) // fgr. child = child.nextSiblingElement(); while(!child.isNull()) { // Is it a comment or the first of one|more elements ? // Remember: if(child.tagName() == "comment") { if(comment_was_parsed) { qCritical() << "The FragmentationPathway did not render " "correctly: problem with " "multiple elements."; m_isValid = false; return m_isValid; } m_comment = child.text(); comment_was_parsed = true; child = child.nextSiblingElement(); continue; } // At this point, yes, if there is still a sibling, then it // has to be one , either alone or the first of multiple. while(!child.isNull()) { FragmentationRuleSPtr frag_rule_sp = std::make_shared(mcsp_polChemDef, child, 1); if(!frag_rule_sp->isValid()) { qCritical() << "The FragmentationPathway did not render " "correctly: problem with " "a FragmentationRule element."; frag_rule_sp.reset(); m_isValid = false; return m_isValid; } m_rules.push_back(frag_rule_sp); child = child.nextSiblingElement(); } } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate FragmentationPathway instance right after " "rendering it from XML element, with errors:" << Utils::joinErrorList(error_list, ", "); return m_isValid; } /*! \brief Formats a string representing this FragmentationPathway instance suitable to use as an XML element. The typical fragmentation pathway element that is generated in this function looks like this: \code a LE -C1O1 0 opt_comment a-fgr-1 +H200 E D F opt_comment a-fgr-2 +H100 F D E opt_comment \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a dynamically allocated string that needs to be freed after use. */ QString FragmentationPathway::formatXmlFgpElement(int offset, const QString &indent) const { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue, with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n").arg(lead).arg(fragEndMap[m_fragEnd]); text += QString("%1%2\n") .arg(lead) .arg(m_formula.getActionFormula()); text += QString("%1%2\n") .arg(lead) .arg(m_monomerContribution); if(!m_comment.isEmpty()) text += QString("%1%2\n").arg(lead).arg(m_comment); for(const FragmentationRuleSPtr &frag_rule_sp : m_rules) text += frag_rule_sp->formatXmlFgrElement(newOffset); // Prepare the lead. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/FragmentationRule.cpp000664 001750 001750 00000056346 15100504560 026433 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/FragmentationRule.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::FragmentationRule \inmodule libXpertMassCore \ingroup PolChemDefGasPhaseChemicalReactions \inheaderfile FragmentationRule.hpp \brief The FragmentationRule class provides a model for specifying gas phase fragmentation rules for refining fragmentation pathway specifications (\l FragmentationPathway) of \l{Oligomer} \l{Sequence}s. Fragmentation rules characterize in more detail the chemical reaction that governs the fragmentation of the polymer in the gas-phase. The rule is a conditional rule. Its logic is based on the presence of specified monomers right at the place of the fragmentation and before or after that precise location. In saccharide chemistry, fragmentations are a very complex topic. This is because a given monomer will fragment according to a given chemistry if it is preceded in the sequence by a monomer of a given identity and according to another chemistry if its direct environment is different. This paradigm is implemented using a sequence environment logic based on conditions that can be formulated thanks to three monomer codes: \list \li The monomer at which the fragmentation takes place (current code); \li The monomer preceeding the current code (previous code); \li The monomer following the current code (next code); \endlist The use of these codes is typically according to this kind of logic: \e{If current monomer is Glu and previous monomer is Gly and next monomer is Arg, then fragmentation should occur according to this formula : "-H2O"}. \sa FragmentationPathway */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::mcsp_polChemDef \brief The PolChemDef (polymer chemistry definition) that is the context in which the Oligomer being fragmented exists. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_name \brief The name of the FragmentationRule. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_prevCode \brief The \l Monomer code of the Monomer located before the actual fragmentation site. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_currCode \brief The \l Monomer code of the Monomer at the actual fragmentation site. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_nextCode \brief The \l Monomer code of the Monomer located after the actual fragmentation site. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_comment \brief A comment associated to the FragmentationRule. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_formula \brief A \l{Formula} instance describing the fragmentation reaction occurring on the Monomer at which the fragmentation occurs. */ /*! \variable MsXpS::libXpertMassCore::FragmentationRule::m_isValid \brief The validity status of this FragmentationRule instance. */ /*! \brief Constructs a fragmentation rule with a number of parameters. \list \li \a pol_chem_def_csp Polymer chemistry definition. Cannot be nullptr. \li \a name Name. Cannot be empty. \li \a prev_code Previous monomer code. Defaults to the null string. \li \a current_code Current monomer code. Defaults to the null string. \li \a next_code Next monomer code. Defaults to the null string. \li \a comment Comment. Defaults to the null string. \li \a formula_string Formula. Defaults to the null string. \endlist Upon setting all the member data, this instance is validated and the member m_isValid is set to the result. */ FragmentationRule::FragmentationRule(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &prev_code, const QString ¤t_code, const QString &next_code, const QString &comment, const QString &formula_string) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_prevCode(prev_code), m_currCode(current_code), m_nextCode(next_code), m_comment(comment), m_formula(formula_string) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a FragmentationRule instance starting from an XML \a element according to \a version and using the reference \a pol_chem_def_csp polymer chemistry definition. This is the current format (FAKE fgr): \code a-fgr-2 +H100 F D E comment here! \endcode The rendering of the CleavageRule instances requires that the PolChemDef be available. \sa renderXmlFgrElement() */ FragmentationRule::FragmentationRule(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, [[maybe_unused]] int version) : mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing FragmentationRule with no PolChemDef."; if(!renderXmlFgrElement(element)) qCritical() << "Failed to fully render or validate the FragmentationRule XML element " "for construction of FragmentationRule instance."; } /*! \brief Constructs a FragmentationRule instance as a copy of \a other. Upon setting all the member data, this instance is validated and the member m_isValid is set to the result. */ FragmentationRule::FragmentationRule(const FragmentationRule &other) : mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_prevCode(other.m_prevCode), m_currCode(other.m_currCode), m_nextCode(other.m_nextCode), m_comment(other.m_comment), m_formula(other.m_formula) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon construction of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Destructs the fragmentation rule. */ FragmentationRule::~FragmentationRule() { } /*! \brief Sets the PolChemDef to \a pol_chem_def_csp. */ void FragmentationRule::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting PolChemDef of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the PolChemDef. */ PolChemDefCstSPtr FragmentationRule::getPolchemDefCstSPtr() const { return mcsp_polChemDef; } /*! \brief Sets the name to \a name. */ void FragmentationRule::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting name of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ const QString & FragmentationRule::getName() { return m_name; } /*! \brief Sets the previous monomer \a code. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void FragmentationRule::setPrevCode(const QString &code) { m_prevCode = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting previous code of FragmentationRule, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the previous monomer code. */ const QString & FragmentationRule::getPrevCode() const { return m_prevCode; } /*! \brief Sets the current monomer \a code. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void FragmentationRule::setCurrCode(const QString &code) { m_currCode = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting current code of FragmentationRule, the " "instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the current monomer code. */ const QString & FragmentationRule::getCurrCode() const { return m_currCode; } /*! \brief Sets the next monomer \a code. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void FragmentationRule::setNextCode(const QString &code) { m_nextCode = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting next code of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the next monomer code. */ const QString & FragmentationRule::getNextCode() const { return m_nextCode; } /*! \brief Sets the \a comment. */ void FragmentationRule::setComment(const QString &comment) { m_comment = comment; } /*! \brief Returns the comment. */ const QString & FragmentationRule::getComment() const { return m_comment; } /*! \brief Sets the \a formula. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void FragmentationRule::setFormula(const Formula &formula) { m_formula = formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting Formula of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Sets the member Formula using \a formula_string. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. */ void FragmentationRule::setFormula(const QString &formula_string) { m_formula = Formula(formula_string); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon setting Formula of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns a const reference to the member \l Formula. */ const Formula & FragmentationRule::getFormulaCstRef() const { return m_formula; } /*! \brief Returns a reference to the member \l Formula. */ Formula & FragmentationRule::getFormulaRef() { return m_formula; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this FragmentationRule instance. Upon setting the member data, this instance is validated and the member m_isValid is set to the result. Returns a reference to this fragmentation rule. */ FragmentationRule & FragmentationRule::operator=(const FragmentationRule &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_prevCode = other.m_prevCode; m_currCode = other.m_currCode; m_nextCode = other.m_nextCode; m_comment = other.m_comment; m_formula = other.m_formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Upon assignment of FragmentationRule, the instance failed to " "validate with errors:" << Utils::joinErrorList(error_list, ", "); return *this; } /*! \brief Returns true if \c this instance and \a other are identical. */ bool FragmentationRule::operator==(const FragmentationRule &other) const { if(&other == this) return true; return mcsp_polChemDef == other.mcsp_polChemDef && m_name == other.m_name && m_prevCode == other.m_prevCode && m_currCode == other.m_currCode && m_nextCode == other.m_nextCode && m_comment == other.m_comment && m_formula == other.m_formula; } /*! \brief Returns true if \c this and \a other are different. Returns the negated result of operator==(). */ bool FragmentationRule::operator!=(const FragmentationRule &other) const { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Validates the FragmentationRule, recording any error as a message in \a error_list_p. The validation involves checking that: \list \li The member PolChemDef cannot be nullptr. \li The name is not empty. \li The previous code is valid if non empty. \li The current code is valid if non empty. \li The next code is valid if non empty. \li The formula validates successfully if non empty. \endlist The m_isValid member is set to true upon success, false otherwise and returned. */ bool FragmentationRule::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "A FragmentationRule with no PolChemDef available cannot " "validate successfully."; error_list_p->push_back( "A FragmentationRule with no PolChemDef available cannot validate " "successfully"); } if(m_name.isEmpty()) { qCritical() << "A FragmentationRule with no name cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with no name cannot validate successfully"); } if(m_prevCode.isEmpty() && m_currCode.isEmpty() && m_nextCode.isEmpty()) { qCritical() << "A FragmentationRule with not a single Monomer code " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with not a single Monomer code cannot validate " "successfully"); } if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_prevCode.isEmpty()) { if(mcsp_polChemDef->getMonomerCstSPtrByCode(m_prevCode) == nullptr) { qCritical() << "A FragmentationRule with an unknown previous monomer code " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with an unknown previous monomer code cannot " "validate " "successfully"); } } if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_currCode.isEmpty()) { if(mcsp_polChemDef->getMonomerCstSPtrByCode(m_currCode) == nullptr) { qCritical() << "A FragmentationRule with an unknown current monomer code " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with an unknown current monomer code cannot " "validate " "successfully"); } } if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_nextCode.isEmpty()) { if(mcsp_polChemDef->getMonomerCstSPtrByCode(m_nextCode) == nullptr) { qCritical() << "A FragmentationRule with an unknown next monomer code " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with an unknown next monomer code cannot " "validate " "successfully"); } } if(m_formula.getActionFormula().isEmpty()) { qCritical() << "A FragmentationRule with no Formula does not make sense and " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with no Formula does not make sense and cannot " "validate " "successfully"); } if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr && !m_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "A FragmentationRule with a non-empty invalid Formula " "cannot validate successfully."; error_list_p->push_back( "A FragmentationRule with a non-empty invalid Formula cannot validate " "successfully"); } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this FragmentationRule. */ bool FragmentationRule::isValid() const { return m_isValid; } //////////////// UTILITIES ///////////////////// /*! \brief Returns a string with the textual representation of this FragmentationRule instance. */ QString FragmentationRule::toString() const { QString text = "Fragmentation rule:\n"; text += QString("name: %1 - prev code: %2 - curr code: %3 - next code: %4 - formula: %5 - comment: %6 - is valid? %7\n").arg(m_name).arg(m_prevCode).arg(m_currCode).arg(m_nextCode).arg(m_formula.getActionFormula(/*with_title*/ true)).arg(m_comment).arg(m_isValid ? "true" : "false"); return text; } /*! \brief Parses the FragmentationRule XML \a element. Upon parsing and validation of the parsed data, the member data are updated, thus essentially initializing this FragmentationRule instance. Returns true if parsing and formula validation were successful, false otherwise. */ bool FragmentationRule::renderXmlFgrElement(const QDomElement &element) { QDomElement child; bool prevSet = false; bool currSet = false; bool nextSet = false; bool commentSet = false; /* The xml node we are in is structured this way: * * * one_rule * +H2O * M * Y * T * opt_comment * * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "fgr" and that * we'll have to go one step down to the first child of the * current node in order to get to the element. * * The DTD: * */ if(element.tagName() != "fgr") return false; child = element.firstChildElement(); if(child.isNull() || child.tagName() != "name") return false; m_name = child.text(); child = child.nextSiblingElement(); if(child.isNull() || child.tagName() != "formula") return false; if(!m_formula.renderXmlFormulaElement(child)) return false; // Since the following items are not obligatory, we have to while() // until we have no more items... child = child.nextSiblingElement(); while(!child.isNull()) { if(child.tagName() == "prev-mnm-code") { if(prevSet) return false; else { m_prevCode = child.text(); prevSet = true; } } else if(child.tagName() == "curr-mnm-code") { if(currSet) return false; else { m_currCode = child.text(); currSet = true; } } else if(child.tagName() == "next-mnm-code") { if(nextSet) return false; else { m_nextCode = child.text(); nextSet = true; } } else if(child.tagName() == "comment") { if(commentSet) return false; else { m_comment = child.text(); commentSet = true; } } child = child.nextSiblingElement(); } ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The FragmentationRule rendered from element is invalid."; } return m_isValid; } /*! \brief Formats a string suitable to use as an XML element. The string is suitable to be used as an XML element in a polymer chemistry definition file. The typical fragmentation rule element that is generated in this function looks like this: \code a-fgr-2 +H100 F D E comment here! \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a dynamically allocated string that needs to be freed after use. */ QString FragmentationRule::formatXmlFgrElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n") .arg(lead) .arg(m_formula.getActionFormula()); if(!m_prevCode.isEmpty()) text += QString("%1%2\n") .arg(lead) .arg(m_prevCode); if(!m_currCode.isEmpty()) text += QString("%1%2\n") .arg(lead) .arg(m_currCode); if(!m_nextCode.isEmpty()) text += QString("%1%2\n") .arg(lead) .arg(m_nextCode); if(!m_comment.isEmpty()) text += QString("%1%2\n").arg(lead).arg(m_comment); // Prepare the lead. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Fragmenter.cpp000664 001750 001750 00000403620 15100504560 025066 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Fragmenter.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Fragmenter \inmodule libXpertMassCore \ingroup PolChemDefAqueousChemicalReactions \inheaderfile Fragmenter.hpp \brief The Fragmenter class provides a model for performing gas phase fragmentation reactions involving \l{FragmentationPathway} objects and \l{Polymer} \l{Sequence}s. The fragmentation process is configured by the member vector of FragmentationConfig instances that may store, for example, all the fragmentation pathways that need to be dealt with in the inner workings of this Fragmenter class. For example, the user might want to perform a series of fragmentation involving pathways b and y for protein fragmentation. \sa FragmentationPathway, FragmentationRule, FragmentationConfig, Ionizer */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::mcsp_polymer \brief The \l Polymer polymer that is being cleaved (digested). */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition that is the context in which the Polymer exists. */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::m_fragmentationConfigs \brief The container of FragmentationConfig instances that collectively configure the fragmentation pathways to implement during the fragmentation process. */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::m_calcOptions \brief The CalcOptions that configure the way masses and formulas are to be computed. */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::m_ionizer \brief The Ionizer that directs the ionization of the Oligomer instances obtained by cleaving the Polymer. */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::m_oligomers \brief The vector of fragment Oligomer instances (product ions) generated as a result of the fragmentation. */ /*! \variable MsXpS::libXpertMassCore::Fragmenter::m_crossLinkedRegions \brief The vector of CrossLinkedRegion that describe the way product ion fragments might involved CrossLink instances. \sa CrossLinkedRegion */ /*! \brief Constructs a Fragmenter instance with a number of parameters. \list \li \a polymer_cqsp The Polymer instance to be fragmented. \li \a pol_chem_def_csp The PolChemDef (polymer chemistry definition) that is the context in which the Polymer exists. \li \a fragmentation_configs The container of FragmentationConfig instances that configure the fragmentation. \li \a calc_options The CalcOptions instance that configures the mass and formula calculations. \li \a ionizer The Ionizer instance that drives the ionization of the Oligomer instances generated by the cleavage. \endlist If polymer_cqsp or pol_chem_def_csp is nullptr, that is a fatal error. */ Fragmenter::Fragmenter( PolymerCstQSPtr polymer_cqsp, PolChemDefCstSPtr pol_chem_def_csp, const std::vector &fragmentation_configs, const CalcOptions &calc_options, const Ionizer &ionizer) : mcsp_polymer(polymer_cqsp), mcsp_polChemDef(pol_chem_def_csp), m_fragmentationConfigs(fragmentation_configs), m_calcOptions(calc_options), m_ionizer(ionizer) { if(mcsp_polymer == nullptr && mcsp_polymer.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(mcsp_polChemDef == nullptr && mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; // qDebug() << "Constructing Fragmenter with CalcOptions:" // << m_calcOptions.toString(); } /*! \brief Constructs Fragmenter instance as a copy of \a other. If polymer_cqsp or pol_chem_def_csp is nullptr, that is a fatal error. */ Fragmenter::Fragmenter(const Fragmenter &other) : mcsp_polymer(other.mcsp_polymer), mcsp_polChemDef(other.mcsp_polChemDef), m_fragmentationConfigs(other.m_fragmentationConfigs), m_calcOptions(other.m_calcOptions), m_ionizer(other.m_ionizer) { if(mcsp_polymer == nullptr && mcsp_polymer.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(mcsp_polChemDef == nullptr && mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; } /*! \brief Desstructs this Fragmenter instance */ Fragmenter::~Fragmenter() { } /*! \brief Adds \a fragmentation_config to the member container of FragmentationConfig instances. */ void Fragmenter::addFragmentationConfig( const FragmentationConfig &fragmentation_config) { m_fragmentationConfigs.push_back(fragmentation_config); } /*! \brief Transfers (using std::move()) all the Oligomer instances from \a source to \a dest. After the transfer, the \a source Oligomer container is cleared since it contains only nullptr items. */ std::size_t Fragmenter::transferOligomers(OligomerCollection &source, OligomerCollection &dest) { std::size_t dest_oligomer_count_before = dest.getOligomersRef().size(); // Move each element from source to dest dest.getOligomersRef().insert( dest.getOligomersRef().end(), std::make_move_iterator(source.getOligomersRef().begin()), std::make_move_iterator(source.getOligomersRef().end())); std::size_t dest_oligomer_count_after = dest.getOligomersRef().size(); std::size_t transferred_count = dest_oligomer_count_after - dest_oligomer_count_before; // Sanity check if(transferred_count != source.getOligomersRef().size()) qFatalStream() << "Programming error. Not all the Oligomers were transferred."; // Now clear the source container which contains the same items as before but // all the shared pointers are now nullptr. source.getOligomersRef().clear(); return transferred_count; } /*! \brief Transfers (using std::move()) \a source_oligomer_sp to \a dest. */ void Fragmenter::transferOligomer(OligomerSPtr &&source_oligomer_sp, OligomerCollection &dest) { dest.getOligomersRef().push_back(std::move(source_oligomer_sp)); } /*! \brief Returns a const reference to the member OligomerCollection instance. */ const OligomerCollection & Fragmenter::getOligomerCollectionCstRef() const { return m_oligomers; } /*! \brief Returns a reference to the member OligomerCollection instance. */ OligomerCollection & Fragmenter::getOligomerCollectionRef() { return m_oligomers; } /*! \brief Performs the actual fragmentation and returns true if successful, false otherwise. All the FragmentationConfig instances in the member container are iterated into and the fragmentation procedure is implemented, storing all the generated fragment Oligomer instances in the member OligomerCollection instance. */ bool Fragmenter::fragment() { // If the polymer sequence is empty, just return. if(!mcsp_polymer->size()) { qCritical() << "The polymer sequence is empty: nothing to fragment."; return true; } // Ensure that the list of fragmentation configs is not empty. if(!m_fragmentationConfigs.size()) { qWarning() << "List of fragmentation configs is empty !"; return false; } // qDebug() << "Number of fragmentation configurations:" // << m_fragmentationConfigs.size(); // Check if we have to account for monomer modifications. If so, DEEP-compute // the mass of all the monomers that are comprised in the sequence range. double mono; double avg; // if(static_cast(m_calcOptions.getMonomerEntities()) & // static_cast(Enums::ChemicalEntity::MODIF)) // Use the overload (see globals.hpp) if(static_cast(m_calcOptions.getMonomerEntities() & Enums::ChemicalEntity::MODIF)) { // qDebug() << "Fragmentation calculations take " // "into account the monomer modifications"; m_calcOptions.setDeepCalculation(true); Polymer::calculateMasses( mcsp_polymer.get(), m_calcOptions, mono, avg, /*reset*/ false); } // Before starting the calculation we ought to know if there are // cross-links in the oligomer to be fragmented and if the user // has asked that these cross-linked be taken into account during // the fragmentation. // If there are cross-links, then we have to deal with // them. The strategy is to first get a list of all the // monomer indices for the monomers in the Oligomer (being // fragmented) that are involved in cross-links. FragmentationConfig fragmentation_config = m_fragmentationConfigs.at(0); std::vector cross_link_indices; std::size_t partials = 0; // qDebug() << "Fragmentation config:" // << fragmentation_config.toString(); mcsp_polymer->crossLinkedMonomersIndicesInRange( fragmentation_config.getStartIndex(), fragmentation_config.getStopIndex(), cross_link_indices, partials); if(static_cast(m_calcOptions.getMonomerEntities() & Enums::ChemicalEntity::CROSS_LINKER) && partials) { qDebug() << "Fragmentation calculations do not\n" "take into account partial cross-links.\n" "These partial cross-links are ignored."; } // We do run into the if block below only if the CrossLinks // should be taken into account (if any) and if there are // at least one CrossLink that is not partial (that is, that is // fully encompassed by the startIndex--stopIndex region). if(static_cast(m_calcOptions.getMonomerEntities() & Enums::ChemicalEntity::CROSS_LINKER) && cross_link_indices.size()) { qDebug() << "Fragmentation calculations take " "into account the cross-links and there are " << cross_link_indices.size() << "cross-links in the selected Oligomer."; // Let's put one of the fragmentation configs (the first) so that // we can get the indices of the oligomer to fragment. FragmentationConfig fragmentation_config = m_fragmentationConfigs.at(0); if(partials) qDebug() << "Fragmentation calculations do not\n" "take into account partial cross-links.\n" "These partial cross-links are ignored."; // Now that we have a container with all of the indices of all the // monomers that are cross-linked, we can iterate in it and create a list // of CrossLinkedRegion instances that will allow us to "segment" the // to-fragment oligomer so as to ease the calculation of product ion // masses. // Sort the indices. std::sort(cross_link_indices.begin(), cross_link_indices.end()); #ifdef QT_DEBUG QString indices_text; for(std::size_t index : cross_link_indices) indices_text += QString("%1\n").arg(index); // qDebug().noquote() // << "Indices of all the Monomer instances involved in " // "cross-linked sequences: \n" // << indices_text; #endif // Now find continuous regions and create a new region each // time we find one. std::size_t first = 0; std::size_t last = 0; std::size_t prev = 0; std::size_t next = 0; std::size_t cross_link_indices_size = cross_link_indices.size(); std::size_t last_cross_link_indices_index = cross_link_indices_size - 1; bool iterated_in_loop = false; for(std::size_t iter = 0; iter < last_cross_link_indices_index; ++iter) { iterated_in_loop = true; // Seed the system only at the first iteration. if(!iter) { first = cross_link_indices.at(iter); last = cross_link_indices.at(iter + 1); // qDebug() << __FILE__ << __LINE__ // << "Seeding with first and last:" << first << "--" // << last; } prev = cross_link_indices.at(iter); next = cross_link_indices.at(iter + 1); if(next - prev == 1) { // We are going on with a continuum. Fine. last = next; // qDebug() << "Elongating continuum:" // << "[" << first << "-" << last << "]"; continue; } else { // There is a gap. Close the previous continuum and // start another one. last = prev; // qDebug() << "Closing continuum:" // << "[" << first << "-" << last << "]"; CrossLinkedRegion region(first, last); // Get the cross-links for the region. std::vector cross_links; partials = 0; mcsp_polymer->crossLinksInRange( first, last, cross_links, partials); if(partials) qDebug() << "Fragmentation calculations do not\n" "take into account partial cross-links.\n" "These partial cross-links are ignored."; // Append the obtained cross-links to the region so // that we finalize its construction. Finally append // the new region to the list. region.appendCrossLinks(cross_links); m_crossLinkedRegions.push_back(region); // Now that we have closed a continuum, start seeding // a new one. first = next; } } // End of // for(std::size_t iter = 0; iter < cross_link_indices_size; ++iter) // qDebug() << "Did iterate in loop ?" << iterated_in_loop; // Only close the pending CrossLinkedRegion if we actually did // iterate in the loop above. if(iterated_in_loop) { // We have to close the last continuum that we could not close // because we ended off the for loop. last = next; // qDebug() << "Closing continuum:" // << "[" << first << "-" << last << "]"; CrossLinkedRegion region(first, last); // Get the cross-links for the region. std::vector cross_links; partials = 0; mcsp_polymer->crossLinksInRange(first, last, cross_links, partials); if(partials) qDebug() << "Fragmentation calculations do not\n" "take into account partial cross-links.\n" "These partial cross-links are ignored."; // Append the obtained cross-links to the region so // that we finalize its construction. Finally append // the new region to the list. region.appendCrossLinks(cross_links); m_crossLinkedRegions.push_back(region); // qDebug() << "Determined" << m_crossLinkedRegions.size() << "regions"; // for(CrossLinkedRegion &cross_linked_region : m_crossLinkedRegions) // qDebug() << "Region [" << cross_linked_region.getStartIndex() << "-" // << cross_linked_region.getStopIndex() << "]"; } } // End of // if(static_cast(m_calcOptions.getMonomerEntities() & // Enums::ChemicalEntity::CROSS_LINKER) && // cross_link_indices.size()) // At this point we have a list of regions that we'll be able to // use to compute the fragment masses (if cross-links apply). // For each fragmentation configs instance in the list, perform the // required fragmentation. for(const FragmentationConfig &fragmentation_config : m_fragmentationConfigs) { if(fragmentation_config.getFragEnd() == Enums::FragEnd::NE) { if(fragmentEndNone(fragmentation_config) == -1) return false; } else if(fragmentation_config.getFragEnd() == Enums::FragEnd::LE) { if(fragmentEndLeft(fragmentation_config) == -1) return false; } else if(fragmentation_config.getFragEnd() == Enums::FragEnd::RE) { if(fragmentEndRight(fragmentation_config) == -1) return false; } else qFatalStream() << "Programming error. Erroneous " "Fragmentation End."; } return true; } /*! \brief Performs the actual fragmentation in the specific case that \a fragmentation_config indicates the fragment Oligomers to be generated contain no Polymer end (like immonium ions in protein gas phase chemistry). Returns the count of produced fragment Oliogomer instances. */ int Fragmenter::fragmentEndNone(const FragmentationConfig &fragmentation_config) { int count = 0; // qDebug().noquote() // << "Now fragmenting according to fragmentation_config:" // << fragmentation_config.toString(); // We are generating fragments that are made of a single monomer, // like in the proteinaceous world we have the immonium ions. // We will need the isotopic data throughout all of this function. IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(isotopic_data_csp == nullptr || isotopic_data_csp.get() == nullptr) qFatalStream() << "Programming error. The isotopic data must be available."; CalcOptions calc_options(m_calcOptions); calc_options.setCapType(Enums::CapType::NONE); // qDebug() << "Now fragmenting END::NONE with calculation options:" // << calc_options.toString(); for(std::size_t iter = fragmentation_config.getStartIndex(); iter < fragmentation_config.getStopIndex() + 1; ++iter) { bool frag_rule_applied = false; // We create an oligomer which is not ionized(false) but that // bears the default ionization rule, because this oligomer // might be later used in places where the ionization rule has // to be valid. For example, one drag and drop operation might // copy this oligomer into a mzLab dialog window where its // ionization rule validity might be challenged. Because this // fragmentation oligomer will be a neutral species, we should // set the level member of the ionization to 0. // The general idea is that we will later create as many different // oligomers as there are requested levels of ionization. So, for the // moment we try to create a "template" oligomer with the ionization rule // set but with the ionization level set to 0. // Old version, see below for Ionizer() in the construction of the // Oligomer. // Ionizer temp_ionizer(m_ionizer); // temp_ionizer.setLevel(0); // Allocate an Oligomer in which we'll update each time the formula // that mirrors the masses of it. CalcOptions fragment_calc_options(calc_options); fragment_calc_options.setIndexRange(iter, iter); OligomerSPtr oligomer1_sp = std::make_shared( mcsp_polymer, "NOT_SET", fragmentation_config.getName() /*fragmentationPathway.m_name*/, mcsp_polymer->modifiedMonomerCount(IndexRangeCollection(iter, iter)), Ionizer(), fragment_calc_options); // The formula of the fragmentation specification should yield a neutral // molecular species, which is then ionized according to the current // Ionizer in the caller context. The levels of this Ionizer are set by // the user and default to a single level of ionization. // The first step is to calculate the masses of the fragment // oligomer without taking into account the ionization, // because we still have other things to account for that // might interfere with the mass of the fragment. So we pass // an invalid temp_ionizer object(upon creation, an Ionizer // is invalid). Ionizer temp_ionizer; if(!oligomer1_sp->calculateMasses(fragment_calc_options, temp_ionizer)) { oligomer1_sp.reset(); return -1; } // qDebug() << "Right after creation with Ionizer() at index:" << iter // << "mass calculation:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG); // Account in the oligomer's formula for the formula of the monomer. // Ask the monomer to actually compute its formula either by taking into // account the modifications or not depending on the calculation options // below. QString monomer_formula_string = mcsp_polymer->getSequenceCstRef() .getMonomerCstSPtrAt(iter) ->calculateFormula(m_calcOptions.getMonomerEntities()); bool ok = false; oligomer1_sp->getFormulaRef().accountFormula( monomer_formula_string, isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_formula_string; return -1; } // Now start getting deep inside the chemistry of the fragmentation // oligomer. if(!fragmentation_config.getFormulaCstRef().getActionFormula().isEmpty()) { Formula temp_formula( fragmentation_config.getFormulaCstRef().getActionFormula()); bool ok = false; // First account the masses, that is MONO and AVG. temp_formula.accountMasses(ok, isotopic_data_csp, oligomer1_sp->getMassRef(Enums::MassType::MONO), oligomer1_sp->getMassRef(Enums::MassType::AVG)); if(!ok) { qCritical() << "Failed to account masses for:" << temp_formula.getActionFormula(); return -1; } // qDebug() << "After accounting fragmentation pathway formula:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG); // Second, account the formula. oligomer1_sp->getFormulaRef().accountFormula( temp_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << temp_formula.getActionFormula(); return -1; } } // qDebug() << "After accounting formula, Oligomer:" // << oligomer1_sp->toString(); // At this moment, the new fragment might be challenged for // the fragmented monomer's contribution. For example, in // nucleic acids, it happens that during a fragmentation, the // base of the fragmented monomer is decomposed and goes // away. This is implemented here with the ability to // tell the fragmenter that upon fragmentation the mass of the // monomer is to be removed. The skeleton mass is then added // to the formula of the fragmentation pattern (FragmentationPathway). int monomer_contribution = fragmentation_config.getMonomerContribution(); if(monomer_contribution) { MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // First the masses. monomer_csp->accountMasses(oligomer1_sp->getMassRef(Enums::MassType::MONO), oligomer1_sp->getMassRef(Enums::MassType::AVG), monomer_contribution); // qDebug() << "After accounting monomer contribution:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG); bool ok = false; // Next the formula. oligomer1_sp->getFormulaRef().accountFormula( monomer_csp->getFormula(), isotopic_data_csp, monomer_contribution, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_csp->getFormula(); oligomer1_sp.reset(); return -1; } } // At this point we should check if the fragmentation // specification includes fragmentation rules that apply to this // fragment. FragmentationConfig is derived from FragmentationPathway. for(const FragmentationRuleSPtr &fragmentation_rule_sp : fragmentation_config.getRulesCstRef()) { // The accounting of the fragmentationrule is performed on a // neutral oligomer, as defined by the fragmentation // formula. Later, we'll have to take into account the // fact that the user might want to calculate fragment m/z values // with z>1. // This round is not for real accounting, but only to check if the // currently iterated fragmentation rule should be accounted for (see // the true that indicates this call is only for checking). double mono; double avg; if(!accountFragmentationRule(fragmentation_rule_sp, /*only for checking*/ true, iter, Enums::FragEnd::NE, mono, avg)) continue; // This is why we first check above if the fragmentation rule was to // be accounted for: each fragmentationrule triggers the creation of a // new oligomer. OligomerSPtr oligomer2_sp = std::make_shared(*oligomer1_sp); // qDebug() << "After copying oligomer1_sp into oligomer2_sp:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG); accountFragmentationRule(fragmentation_rule_sp, false, iter, Enums::FragEnd::NE, oligomer2_sp->getMassRef(Enums::MassType::MONO), oligomer2_sp->getMassRef(Enums::MassType::AVG)); // qDebug() << "After accounting fragmentation rule:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG); bool ok = false; oligomer2_sp->getFormulaRef().accountFormula( fragmentation_rule_sp->getFormulaCstRef().getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << fragmentation_rule_sp->getFormulaCstRef().getActionFormula(); oligomer1_sp.reset(); oligomer2_sp.reset(); return -1; } // Let the following steps know that we actually succeeded in // preparing a fragment oligomer with a fragmentation rule applied. frag_rule_applied = true; // At this point we have the fragment oligomer in a // neutral state (the fragmentation specification specifies a formula // to create a neutral fragmentation product). This is so that we can // later charge the ions as many times as requested by the user. // We still have to account for potential formulas set to the // FragmentationConfig, like when the user asks that for each product // ion -H2O or -NH3 be accounted for, // which happens all the time when doing peptide fragmentations. // These formulas are stored in the m_formulas member of // FragmentationConfig. // And once we have done this, we'll still have to actually charge the // fragments according to the FragmentationConfig's // [m_charge_level_start--m_stopIonizeLevel] range. // Each supplementary step above demultiplies the "versions" of the // fragment Oligomer currently created (oligomer2_sp). All the newly // created "versions" // will need to be stored in a container // (formula_variant_oligomers). So, for the moment copy the current // oligomer into a template oligomer that will be used as a template // to create all the "variant" oligomers. OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer2_sp); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2#(%3)") .arg(fragmentation_config.getName()) .arg(mcsp_polymer->getSequenceCstRef() .getMonomerCstSPtrAt(iter) ->getCode()) .arg(fragmentation_rule_sp->getName()); // qDebug() << "Short name of oligomer:" << name; int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); // qDebug() << "Long name of oligomer:" << name_with_charge; template_oligomer_for_formula_variants_sp->setName(name_with_charge); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "There are now" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers"; // We now have a container of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // First off, we can finally delete the grand template oligomer. oligomer2_sp.reset(); if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. // std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; } // End of // for(const FragmentationRuleSPtr &fragmentation_rule_sp : // fragmentation_config.getRulesCstRef()) // We are here because of two reasons: // 1. because the container of fragmentation_rule_sp was empty, in which // case we still have to validate and terminate the oligomer1 // (frag_rule_applied is false); // 2. because we finished dealing with the container of // fragmentation_rule_sp, in which case we ONLY add oligomer1 to the list // of fragments if none of the fragmentationrules analyzed above gave a // successfully generated fragment(frag_rule_applied is false). if(!frag_rule_applied) { // At this point we have a single fragment oligomer because we did not // had to apply any fragmentation rule, and thus have generated no // variant of it. We may have formulas to apply, as there might be // formulas in FragmentationConfig. // So, first create an oligomer with the "default" // fragmentation specification-driven neutral state (that // is, charge = 0). OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer1_sp); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2") .arg(fragmentation_config.getName()) .arg(mcsp_polymer->getSequenceCstRef() .getMonomerCstSPtrAt(iter) ->getCode()); // qDebug() << "Short name of oligomer:" << name; int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); // qDebug() << "Long name of oligomer:" << name_with_charge; template_oligomer_for_formula_variants_sp->setName(name_with_charge); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "There are now" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers"; // We now have a container of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // First off, we can finally delete the grand template // oligomer (oligomer with no fragmentation rules applied). oligomer1_sp.reset(); if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; count += transferred_count; } // End of // if(!frag_rule_applied) else // (frag_rule_applied == true) { // There were fragmentation rule(s) that could be // successfully applied. Thus we already have created the // appropriate oligomers. Simply delete the template // oligomer. oligomer1_sp.reset(); } } // End of // for (int iter = fragmentation_config.getStartIndex(); // fragmentation_config.getStopIndex() + 1; ++iter) return count; } /*! \brief Performs the actual fragmentation in the specific case that \a fragmentation_config indicates the fragment Oligomers to be generated contain the left Polymer end (like b ions in protein gas phase chemistry). Returns the count of produced fragment Oliogomer instances. */ int Fragmenter::fragmentEndLeft(const FragmentationConfig &fragmentation_config) { int count = 0; std::size_t number = 0; bool ok; double mono = 0; double avg = 0; // qDebug().noquote() << "Now fragmenting according to fragmentation_config:" // << fragmentation_config.toString(); // Formula to hold the summative result of accounting all of the residues // making the residual chain of the fragmentation ion product. Thus, this // formula is to be incremented at the beginning of each for loop iteration // below. Formula residue_chain_formula; residue_chain_formula.clear(); // We will need the isotopic data throughout all of this function. IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // If the crosslinks are to be taken into account, then make a local copy of // the m_crossLinkedRegions because we are going to remove items from it // during the calculation of the fragments and we do not want to modify the // contents of the original list (remember that this fragmenter might be // created not to perform a single fragmentation, but a set of // fragmentations). std::vector local_cross_linked_regions = m_crossLinkedRegions; // At this point we can start making the calculations of the // fragments. Because we are generating fragments that contain the // left part of the oligomer, we iterate in the // fragmentation_config.getStartIndex() --> // fragmentation_config.getStopIndex() direction. // The general idea of the for loop below is that we iterate in the // [startIndex-stopIndex) range of the polymer that describes the oligomer to // be fragmented. Each iteration adds one monomer. The code below is thus // sectioned into parts that are additive between one iteration and another // and then parts that are renewed each time. // The for loop below is designed to construct product ion oligomers that // start at the left end of the precursor ion (a ions, for example, in // protein chemistry). // If the peptide precursor ion is MAMISGMSGR, for example, // The first pass over the loop below, creates fragment // M // That fragment will have its masses (and chemical formula) computed like so: // 1. the mass/formula of the monomer 'M' // 2. the mass/formula of the CrossLink instances found being // encompassed by the startIndex--stopIndex length of the fragment. // Obviously, for this first M, that is not going to be happening. // Points 1 and 2 above produce data that are then incremented during // the next loop iteration, so that we add the masses for monomer 'A' // in the example sequence at the second iteration and then of 'M' at the // third... // But then, at each iteration (first 'M', then 'A', then 'M' and so // on...), there are calculations that will not apply at each iteration. // For example, the chemical formula that describes the fragmentation // chemistry (for a fragments, -CHO) should be applied only once for all of // the fragments that are elongating through the iteration in the loop. // So, at each iteration, we perform the summed calculations 1. and 2. above, // then we make a copy of the obtained results and we apply the // only-once-per-oligomer calculations to these copies. // One very important concept here: the Oligomer instances that represent the // fragmentation product-ions cannot be represented fully: in particular, // the elementalComposition() function cannot be used to compute the formula // of the fragmentation oligomer just using CalcOptions (index range data) // because the Oligomer object does not know anything of the fragmentation // rules or added formula (+H2O or -NH3, for example). So we rely on the // member Oligomer::m_formula of Oligomer for this. for(std::size_t iter = fragmentation_config.getStartIndex(); iter < fragmentation_config.getStopIndex(); ++iter, ++number) { // This is the common part that will be incremented at each iteration, // thus first creating a fragment of one single monomer (index // startIndex), then at the second iteration creating an oligomer of 2 // monomers, [startIndex, startIndex+1] and so on. The formula that gets // incremented at each iteration with the monomer's formula is // residue_chain_formula. bool frag_rule_applied = false; // Get a pointer to the polymer's monomer at index iter. MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // The accountMasses function below automatically accounts for the monomer // modification(s), if any and if the calculation options do require that // these be accounted for. monomer_csp->accountMasses(mono, avg, 1); // Ask the monomer to actually compute its formula either by taking into // account the modifications or not depending on the calculation options // below. QString monomer_formula_string = monomer_csp->calculateFormula(m_calcOptions.getMonomerEntities()); residue_chain_formula.accountFormula( monomer_formula_string, isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_formula_string; return -1; } // qDebug() << "Accounted masses for monomer:" << monomer_csp->getCode() // << "at index:" << iter << "mono:" << mono << "avg:" << avg // << "with formula:" << residue_chain_formula.getActionFormula(); // If we are to take into account the cross-links, we ought to // take them into account here *once* and then remove them // from the local_cross_linked_regions so that we do not take them // into account more than once. // [3] [4] [5] [6] [9] [11] // o---o---o---o---o--o---o---o---o---o---o---o---o---o---o // | |__| | | | | | // | +-----------+ +-------+ // | | // +------------------+ // // // In the example above, there are two cross-linked regions: [3--9] // and [11--13]. std::vector::iterator the_iterator = local_cross_linked_regions.begin(); // Note below how we need to recompute the end iterator at each loop // iteration because we erase items in the loop and thus the // end iterator gets invalidated. while(the_iterator != local_cross_linked_regions.end()) { CrossLinkedRegion &cross_linked_region = *the_iterator; // Because we are generating fragments that increase in size at each // iteration from the left end, we know a given fragment encompasses // fully a region if its right end (that is the current iteration // 'iter') is at least equal to the right end (stop index) of a given // region. if(cross_linked_region.getStopIndex() == iter) { // We are iterating in a fragment monomer index (iter) that // corresponds to the right end side of a region. Since we already // have gone through all the monomer positions at the left of // 'iter', by essence we know we are constructing a fragment that // encompasses fully the region. // In this case, we have to take into account the cross-links // that are referenced in the region. // qDebug() << "There are" // << cross_linked_region.getCrossLinksCstRef().size() // << "cross-links involved in the current region."; std::size_t iteration_count_for_debugging = 0; for(const CrossLinkSPtr &cross_link_sp : cross_linked_region.getCrossLinksCstRef()) { ++iteration_count_for_debugging; // qDebug() << "At iteration count" // << iteration_count_for_debugging // << "going to account for masses for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName(); cross_link_sp->accountMasses(mono, avg, 1); // qDebug() << "Accounted masses for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << "mono:" << mono << "avg:" << avg; // The cross-linker formula might be empty. if(!cross_link_sp->getCrossLinkerCstSPtr() ->getFormula() .isEmpty()) { // qDebug() // << "Now accounting for the cross-link formula:" // << cross_link_sp->getCrossLinkerCstSPtr()->getFormula(); residue_chain_formula.accountFormula( cross_link_sp->getCrossLinkerCstSPtr()->getFormula(), isotopic_data_csp, 1, ok); if(!ok) { qWarning() << "Failed to account formula:" << cross_link_sp->getCrossLinkerCstSPtr() ->getFormula(); return -1; } // qDebug() // << "Accounted formula for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << residue_chain_formula.getActionFormula(); } } the_iterator = local_cross_linked_regions.erase(the_iterator); } // End of // if(cross_linked_region.getStopIndex() == iter) else ++the_iterator; } // End of // while (the_iterator != the_end_iterator) #if 0 // Old QList-based version // Iterate in the local_cross_linked_regions (do that in reverse // order because we'll have at some point to have to remove // items) and... int jter = local_cross_linked_regions.size() - 1; while(jter >= 0) { // ... for each item in it ask if the region encompasses // the current monomer index (value of iter).... CrossLinkedRegion region = local_cross_linked_regions.at(jter); if(region.getStopIndex() == iter) { // ... if so, iterate in the list of cross-links that // is stored in the CrossLinkedRegion... const QList &crossLinkList = region.crossLinkList(); for(int kter = 0; kter < crossLinkList.size(); ++kter) { // ... and for each cross-link, account its mass // in the fragment (that is, ponderable)... CrossLink *crossLink = crossLinkList.at(kter); crossLink->accountMasses(&ponderable, 1); residue_chain_formula.accountFormula( crossLink->formula(), isotopic_data_csp, ok, 1); if(!ok) { qWarning() << "Failed to account formula:" << crossLink->formula(); return -1; } } // ... and remove+delete the CrossLinkedRegion from // the list so that we are sure we do not take that // cross-link into account more than once. delete local_cross_linked_regions.takeAt(jter); } --jter; } #endif // Now start a new section that will be specific to this iteration in the // for loop. We have summed the masses/formulas of the monomers up to this // iteration and we need to finalize the current fragment with // calculations that apply only once for each fragment. We thus create a // copy of the initial residual-chain-only formula and of the masses. // These data are updated each time a new aspect of the fragmentation // chemistry will be accounted for. double frag_chemistry_mono = mono; double frag_chemistry_avg = avg; // Make a copy of the residual chain-only formula so that we can aggregate // specific formula components later. Formula frag_chemistry_formula(residue_chain_formula); // qDebug() << "Now starting fragment position-specific calculations with " // "masses mono:" // << frag_chemistry_mono << "avg:" << frag_chemistry_avg // << "with formula:" << frag_chemistry_formula.getActionFormula(); // FragmentationConfig is a FragmentationPathway that holds the formula // that makes a fragment out of an oligomer. if(!fragmentation_config.getFormulaCstRef().getActionFormula().isEmpty()) { Formula frag_specific_formula( fragmentation_config.getFormulaCstRef().getActionFormula()); bool ok = false; // First account the masses, that is MONO and AVG. frag_specific_formula.accountMasses( ok, isotopic_data_csp, frag_chemistry_mono, frag_chemistry_avg); if(!ok) { qCritical() << "Failed to account masses for:" << frag_specific_formula.getActionFormula(); return -1; } // Second, account the formula. frag_chemistry_formula.accountFormula( frag_specific_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << frag_specific_formula.getActionFormula(); return -1; } // qDebug() << "Accounted masses for fragmentation formula:" // << frag_specific_formula.getActionFormula() // << "mono:" << frag_chemistry_mono // << "avg:" << frag_chemistry_avg << "and with formula:" // << frag_chemistry_formula.getActionFormula(); } // Account for the left cap since we are dealing with fragments that // retain the left end. Formula left_cap_formula = Formula(mcsp_polChemDef->getLeftCap()); left_cap_formula.accountMasses( ok, isotopic_data_csp, frag_chemistry_mono, frag_chemistry_avg); if(!ok) { qCritical() << "Failed to account masses for left cap:" << left_cap_formula.getActionFormula(); return -1; } frag_chemistry_formula.accountFormula( left_cap_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account left cap formula:" << left_cap_formula.getActionFormula(); return -1; } // qDebug() << "Accounted masses for left cap formula:" // << left_cap_formula.getActionFormula() // << "mono:" << frag_chemistry_mono << "avg:" << frag_chemistry_avg // << "and with formula:" // << frag_chemistry_formula.getActionFormula(); // Same of left end chemical modification only if the left end of this // fragment oligomer actually encompasses the real left end monomer // of the polymer (that is the start index is actually 0). if(static_cast(m_calcOptions.getPolymerEntities() & Enums::ChemicalEntity::LEFT_END_MODIF) && !fragmentation_config.getStartIndex()) { Polymer::accountEndModifMasses(mcsp_polymer.get(), Enums::ChemicalEntity::LEFT_END_MODIF, frag_chemistry_mono, frag_chemistry_avg); frag_chemistry_formula.accountFormula( mcsp_polymer->getLeftEndModifCstRef().getFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account left end formula:" << mcsp_polymer->getLeftEndModifCstRef().getFormula(); return -1; } // qDebug() << "Accounted masses for left end formula:" // << mcsp_polymer->getLeftEndModifCstRef().getFormula() // << "mono:" << frag_chemistry_mono // << "avg:" << frag_chemistry_avg << "and with formula:" // << frag_chemistry_formula.getActionFormula(); } // At this moment, we have crafted the skeleton of the fragment oligomer // but we still potentially need to account for a number of chemical // entities. // We will account for a number of formulas later, for the moment make the // oligomer as naked as possible. We'll dress it with formulas as we go. // Naked here means just account for the monomers (that is, the monomers // and not the monomers along with the end caps, the charge and so on). CalcOptions local_calc_options(m_calcOptions); local_calc_options.setCapType(Enums::CapType::NONE); // qDebug() << "Going to use CalcOptions:" << // local_calc_options.toString() // << "to initialize Oligomer."; // We need to set the proper IndexRange for the current iteration in the // sequence of the oligomer being fragmented. We know the start index is // certainly not changing because we are doing LE fragmentation. The // stop index, instead, changes with each iteration of this loop. local_calc_options.setIndexRange(fragmentation_config.getStartIndex(), iter); // Create a fragmentation oligomer by constructing it with the // data that signal what oligomer it is, but for the moment // empty as far as the formula and the masses are concerned. OligomerSPtr oligomer1_sp = std::make_shared( mcsp_polymer, "NOT_SET", fragmentation_config.getName() /*fragmentationPathway.m_name*/, mcsp_polymer->modifiedMonomerCount( IndexRangeCollection(fragmentation_config.getStartIndex(), iter)), Ionizer(), local_calc_options); // Now set the masses that were additively crafted along the iterations // in this loop: oligomer1_sp->setMasses(frag_chemistry_mono, frag_chemistry_avg); // Now set the formula of the oligomer: set its constructed-empty // formula to the formula that resulted from all the previous steps. oligomer1_sp->getFormulaRef().accountFormula( frag_chemistry_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << frag_chemistry_formula.getActionFormula(); oligomer1_sp.reset(); return -1; } // qDebug() << "The oligomer1_sp has been created using " // "data crafted up to now and has masses:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << oligomer1_sp->getFormulaRef().getActionFormula() // << "Now accounting (if set) for monomer contribution."; // At this moment, the new fragment might be challenged for the // fragmented monomer's side chain contribution. For example, in // nucleic acids, it happens that during a fragmentation, the // base of the fragmented monomer is decomposed and goes // away. This is implemented in massXpert with the ability to // tell the fragmenter that upon fragmentation the mass of the // monomer is to be removed. The skeleton mass is then added to // the formula of the fragmentation pattern. int monomer_contribution = fragmentation_config.getMonomerContribution(); if(monomer_contribution) { MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // First the masses. monomer_csp->accountMasses(oligomer1_sp->getMassRef(Enums::MassType::MONO), oligomer1_sp->getMassRef(Enums::MassType::AVG), monomer_contribution); bool ok = false; // Next the formula. oligomer1_sp->getFormulaRef().accountFormula( monomer_csp->getFormula(), isotopic_data_csp, monomer_contribution, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_csp->getFormula(); oligomer1_sp.reset(); return -1; } // qDebug() << "The oligomer1_sp after side chain contribution " // "has masses:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG) // << "updated formula:" // << oligomer1_sp->getFormulaRef().getActionFormula(); } // At this point we should check if the fragmentation // pathway includes fragmentation rules that apply to this // fragment. FragmentationConfig is derived from FragmentationPathway. for(const FragmentationRuleSPtr &fragmentation_rule_sp : fragmentation_config.getRulesCstRef()) { // The accounting of the fragmentationrule is performed on a // neutral oligomer, as defined by the fragmentation // formula. Later, we'll have to take into account the // fact that the user might want to calculate fragment m/z values // with z>1. // This round is not for real accounting, but only to check of the // currently iterated fragmentation rule should be accounted for (see // the true that indicates this call is only for checking). double mono; double avg; if(!accountFragmentationRule(fragmentation_rule_sp, /*only for checking*/ true, iter, Enums::FragEnd::LE, mono, avg)) continue; // This is why we first check above if the fragmentation rule was to // be accounted for: each fragmentationrule triggers the creation of a // new oligomer. OligomerSPtr oligomer2_sp = std::make_shared(*oligomer1_sp); // qDebug() << "The oligomer2_sp has been copied from " // "oligomer1_sp and has masses:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << oligomer2_sp->getFormulaRef().getActionFormula(); accountFragmentationRule(fragmentation_rule_sp, false, iter, Enums::FragEnd::LE, oligomer2_sp->getMassRef(Enums::MassType::MONO), oligomer2_sp->getMassRef(Enums::MassType::AVG)); bool ok = false; oligomer2_sp->getFormulaRef().accountFormula( fragmentation_rule_sp->getFormulaCstRef().getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << fragmentation_rule_sp->getFormulaCstRef().getActionFormula(); oligomer1_sp.reset(); oligomer2_sp.reset(); return -1; } // qDebug() << "The oligomer2_sp after accounting for " // "fragmentation rule" // << fragmentation_rule_sp->getName() << "has masses:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG) // << "updated formula:" // << oligomer2_sp->getFormulaRef().getActionFormula(); // Let the following steps know that we actually succeeded in // preparing an oligomer (oligonucleotide, for example) with a // fragmentation rule applied. frag_rule_applied = true; // At this point we have the fragment oligomer in a // neutral state (the fragmentation specification specifies a formula // to create a neutral fragmentation product). This is so that we can // later charge the ions as many times as requested by the user. // We still have to account for potential formulas set to the // FragmentationConfig, like when the user asks that for each product // ion -H2O or -NH3 be accounted for, // which happens all the time when doing peptide fragmentations. // These formulas are stored in the m_formulas member of // FragmentationConfig. // And once we have done this, we'll still have to actually charge the // fragments according to the FragmentationConfig's // [m_startIonizeLevel--m_stopIonizeLevel] range. // Each supplementary step above demultiplies the "versions" of the // fragment Oligomer currently created (oligomer2_sp). All the newly // created "versions" // will need to be stored in a container // (formula_variant_oligomers). So, for the moment copy the current // oligomer into a template oligomer that will be used as a template // to create all the "variant" oligomers. OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer2_sp); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2#(%3)") .arg(fragmentation_config.getName()) .arg(number + 1) .arg(fragmentation_rule_sp->getName()); int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); template_oligomer_for_formula_variants_sp->setName(name_with_charge); // qDebug() << "The template_oligomer_for_formula_variants_sp is created" // "by copying over oligomer2_sp. It now has a new name:" // << fragmentation_rule_sp->getName() << " and has masses:" // << "mono:" // << template_oligomer_for_formula_variants_sp->getMass( // Enums::MassType::MONO) // << "avg:" // << template_oligomer_for_formula_variants_sp->getMass( // Enums::MassType::AVG) // << "updated formula:" // << template_oligomer_for_formula_variants_sp->getFormulaRef() // .getActionFormula(); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "There are now" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers"; // We now have a container of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // qDebug() << "We now got" // << ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers:\n" // << ionization_variant_oligomers.toString(); // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // qDebug() << "We still have" // << ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; // First off, we can finally delete the grand template oligomer. oligomer2_sp.reset(); if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; // qDebug().noquote() // << "And now we have" << m_oligomers.getOligomersCstRef().size() // << "oligomers in total, listed below:\n" // << m_oligomers.toString(); count += transferred_count; } // End of // for(const FragmentationRuleSPtr &fragmentation_rule_sp : // fragmentation_config.getRulesCstRef()) // We are here because of two possible reasons: // 1. because the container of fragmentation_rule_sp was empty, in which // case we still have to validate and terminate the oligomer1 // (frag_rule_applied is false); // 2. because we finished dealing with the container of // fragmentation_rule_sp, in which case we ONLY add oligomer1 to the list // of fragments if none of the fragmentationrules analyzed above gave a // successfully generated fragment(frag_rule_applied is false). if(!frag_rule_applied) { // At this point we have a single fragment oligomer because we did not // had to apply any fragmentation rule, and thus have generated no // variant of it. We may have formulas to apply, as there might be // formulas in FragmentationConfig. // qDebug() << "No FragmentationRule had to be applied. We thus have " // "a single oligomer at the moment."; // So, first create an oligomer with the "default" // fragmentation specification-driven neutral state (that // is, charge = 0). OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer1_sp); // qDebug() // << "Created copy of oligomer1_sp as " // "template_oligomer_for_formula_variants_sp for accounting " // "formulas, like +H2O or -NH3, for example, with masses:" // << "mono:" // << template_oligomer_for_formula_variants_sp->getMass( // Enums::MassType::MONO) // << "avg:" // << template_oligomer_for_formula_variants_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << template_oligomer_for_formula_variants_sp->getFormulaCstRef() // .getActionFormula(); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2") .arg(fragmentation_config.getName()) .arg(number + 1); int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); template_oligomer_for_formula_variants_sp->setName(name_with_charge); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // qDebug() << "Now accounting (if any) for added formulas, like +H2O " // "or -NH3 or any user-defined formula."; // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "After accounting (if any) for formulas, there are" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers."; // We now have a list of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. // qDebug() << "Now accounting for the ionization level(s)."; OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // qDebug().noquote() // << "We now got" // << ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers:\n" // << ionization_variant_oligomers.toString(); // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // qDebug() << "We still have" // << ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // First off, we can finally delete the grand template // oligomer (oligomer with no fragmentation rules applied). oligomer1_sp.reset(); // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; // qDebug().noquote() // << "And now we have" << m_oligomers.getOligomersCstRef().size() // << "oligomers in total, listed below:\n" // << m_oligomers.toString(); count += transferred_count; } // End of // if(!frag_rule_applied) else // (frag_rule_applied == true) { // There were fragmentation rule(s) that could be // successfully applied. Thus we already have created the // appropriate oligomers. Simply delete the template // oligomer. oligomer1_sp.reset(); } } // End of // for (int iter = fragmentation_config.getStartIndex(); // iter < fragmentation_config.getStopIndex() + 1; ++iter, ++count) return count; } /*! \briefPerforms the actual fragmentation in the specific case that \a fragmentation_config indicates the fragment Oligomers to be generated contain the right Polymer end (like y ions in protein gas phase chemistry). Returns the count of produced fragment Oliogomer instances. */ int Fragmenter::fragmentEndRight(const FragmentationConfig &fragmentation_config) { int count = 0; std::size_t number = 0; bool ok; double mono = 0; double avg = 0; // qDebug().noquote() << "Now fragmenting according to fragmentation_config:" // << fragmentation_config.toString(); // Formula to hold the summative result of accounting all of the residues // making the residual chain of the fragmentation ion product. Thus, this // formula is to be incremented at the beginning of each for loop iteration // below. Formula residue_chain_formula; residue_chain_formula.clear(); // We will need the isotopic data throughout all of this function. IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // If the crosslinks are to be taken into account, then make a local copy of // the m_crossLinkedRegions because we are going to remove items from it // during the calculation of the fragments and we do not want to modify the // contents of the original list (remember that this fragmenter might be // created not to perform a single fragmentation, but a set of // fragmentations). std::vector local_cross_linked_regions = m_crossLinkedRegions; // See the fragmentEndLeft() very detailed explanations above. for(std::size_t iter = fragmentation_config.getStopIndex(); iter > fragmentation_config.getStartIndex(); --iter, ++number) { // This is the common part that will be incremented at each iteration, // thus first creating a fragment of one single monomer (index // stopIndex), then at the second run creating an oligomer of 2 monomers, // [stopIndex -1, stopIndex] and so on. The formula that gets incremented // at each iteration with the monomer's formula is residue_chain_formula. bool frag_rule_applied = false; // Get a pointer to the polymer's monomer at index iter. MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // The accountMasses function below automatically accounts for the monomer // modification(s), if any and if the calculation options do required that // these be accounted for. monomer_csp->accountMasses(mono, avg, 1); // Ask the monomer to actually compute its formula either by taking into // account the modifications or not depending on the calculation options // below. QString monomer_formula_string = monomer_csp->calculateFormula(m_calcOptions.getMonomerEntities()); residue_chain_formula.accountFormula( monomer_formula_string, isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_formula_string; return -1; } // qDebug() << "Accounted masses for monomer:" << monomer_csp->getCode() // << "at index:" << iter << "mono:" << mono << "avg:" << avg // << "with formula:" << residue_chain_formula.getActionFormula(); // qDebug() << "Accounted formula for monomer:" // << residue_chain_formula.getActionFormula(); // If we are to take into account the cross-links, we ought to // take them into account here *once* and then remove them // from the local_cross_linked_regions so that we do not take them // into account more than once. // [3] [4] [5] [6] [9] [11] // o---o---o---o---o--o---o---o---o---o---o---o---o---o---o // | |__| | | | | | // | +-----------+ +-------+ // | | // +------------------+ // // // In the example above, there are two cross-linked regions: [3--9] // and [11--13]. // Iterate in the crossLinkedRegionList (do that in reverse // order because we'll have at some point to have to remove // items) and... std::vector::iterator the_iterator = local_cross_linked_regions.begin(); // Note below how we need to recompute the end iterator at each loop // iteration because we erase items in the loop and thus the // end iterator gets invalidated. while(the_iterator != local_cross_linked_regions.end()) { CrossLinkedRegion &cross_linked_region = *the_iterator; // Because we are generating fragments that increase in size at each // iteration from the right end, we know a given fragment encompasses // fully a region if its left end (that is the current iteration // 'iter') is at least equal to the left end (start index) of a given // region. if(cross_linked_region.getStartIndex() == iter) { // We are iterating in a fragment monomer index (iter) that // corresponds to the left end side of a region. Since we already // have gone through all the monomer positions at the right of // 'iter', by essence we know we are constructing a fragment that // encompasses fully the region. // In this case, we have to take into account the cross-links // that are referenced in the region. // qDebug() << "There are" // << cross_linked_region.getCrossLinksCstRef().size() // << "cross-links involved in the current region."; std::size_t iteration_count_for_debugging = 0; for(const CrossLinkSPtr &cross_link_sp : cross_linked_region.getCrossLinksCstRef()) { ++iteration_count_for_debugging; // qDebug() << "At iteration count" // << iteration_count_for_debugging // << "going to account for masses for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName(); cross_link_sp->accountMasses(mono, avg, 1); // qDebug() << "Accounted masses for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << "mono:" << mono << "avg:" << avg; // The cross-linker formula might be empty. if(!cross_link_sp->getCrossLinkerCstSPtr() ->getFormula() .isEmpty()) { // qDebug() // << "Now accounting for the cross-link formula:" // << cross_link_sp->getCrossLinkerCstSPtr()->getFormula(); residue_chain_formula.accountFormula( cross_link_sp->getCrossLinkerCstSPtr()->getFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << cross_link_sp->getCrossLinkerCstSPtr() ->getFormula(); return -1; } // qDebug() // << "Accounted formula for cross-link:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << residue_chain_formula.getActionFormula(); } } the_iterator = local_cross_linked_regions.erase(the_iterator); } // End of // if(cross_linked_region.getStopIndex() == iter) else ++the_iterator; } // End of // while (the_iterator != the_end_iterator) // Now start a new section that will be specific to this iteration in the // for loop. We have summed the masses/formulas of the monomers up to this // iteration and we need to finalize the current fragment with // calculations that apply only once for each fragment. We thus create a // copy of the initial residual-chain-only formula and of the masses. // These data are updated each time a new aspect of the fragmentation // chemistry will be accounted for. double frag_chemistry_mono = mono; double frag_chemistry_avg = avg; Formula frag_chemistry_formula(residue_chain_formula); // FragmentationConfig is a FragmentationPathway that holds the formula // that makes a fragment out of an oligomer. if(!fragmentation_config.getFormulaCstRef().getActionFormula().isEmpty()) { Formula frag_specific_formula( fragmentation_config.getFormulaCstRef().getActionFormula()); bool ok = false; // First account the masses, that is MONO and AVG. frag_specific_formula.accountMasses( ok, isotopic_data_csp, frag_chemistry_mono, frag_chemistry_avg); if(!ok) { qCritical() << "Failed to account masses for:" << frag_specific_formula.getActionFormula(); return -1; } // Second, account the formula. frag_chemistry_formula.accountFormula( frag_specific_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << frag_specific_formula.getActionFormula(); return -1; } // qDebug() << "Accounted masses for fragmentation formula:" // << frag_specific_formula.getActionFormula() // << "mono:" << frag_chemistry_mono // << "avg:" << frag_chemistry_avg << "and with formula:" // << frag_chemistry_formula.getActionFormula(); } // Account for the right cap since we are dealing with fragments that // retain the right end. Formula right_cap_formula = Formula(mcsp_polChemDef->getRightCap()); right_cap_formula.accountMasses( ok, isotopic_data_csp, frag_chemistry_mono, frag_chemistry_avg); if(!ok) { qCritical() << "Failed to account masses for right cap:" << right_cap_formula.getActionFormula(); return -1; } frag_chemistry_formula.accountFormula( right_cap_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account right cap formula:" << right_cap_formula.getActionFormula(); return -1; } // qDebug() << "Accounted masses for right cap formula:" // << right_cap_formula.getActionFormula() // << "mono:" << frag_chemistry_mono << "avg:" << frag_chemistry_avg // << "and with formula:" // << frag_chemistry_formula.getActionFormula(); // Same of right end chemical modification only if the right end of this // fragment oligomer actually encompasses the real left end monomer // of the polymer (that is the end index is actually size() - 1). if(static_cast(m_calcOptions.getPolymerEntities() & Enums::ChemicalEntity::RIGHT_END_MODIF) && fragmentation_config.getStopIndex() == mcsp_polymer->size() - 1) { Polymer::accountEndModifMasses(mcsp_polymer.get(), Enums::ChemicalEntity::RIGHT_END_MODIF, frag_chemistry_mono, frag_chemistry_avg); frag_chemistry_formula.accountFormula( mcsp_polymer->getRightEndModifCstRef().getFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account right end formula:" << mcsp_polymer->getRightEndModifCstRef().getFormula(); return -1; } // qDebug() << "Accounted masses for right end formula:" // << mcsp_polymer->getRightEndModifCstRef().getFormula() // << "mono:" << frag_chemistry_mono // << "avg:" << frag_chemistry_avg << "and with formula:" // << frag_chemistry_formula.getActionFormula(); } // The polymer chemistry definition defines a formula for the // FragmentationPathway that yields a fragment oligomer having no // charge: in a neutral state. // The general idea is that we will later create as many different // oligomers as there are requested levels of ionization. So, for the // moment we try to create a "template" oligomer with the ionization rule // set but with the ionization level set to 0 and false for the ionization // status. // We will account for a number of formulas later, for the moment make the // oligomer as naked as possible. We'll dress it with formulas as we go. // Naked here means just account for the monomers (that its the monomers // and not the monomers along with the end caps, the charge and so on). CalcOptions local_calc_options(m_calcOptions); local_calc_options.setCapType(Enums::CapType::NONE); // qDebug() << "Going to use CalcOptions:" << local_calc_options.toString() // << "to initialize Oligomer."; // We need to set the proper IndexRange for the current iteration in the // sequence of the oligomer being fragmented. We know the stop index is // certainly not changing because we are doing RE fragmentation. The // start index, instead, changes with each iteration of this loop. local_calc_options.setIndexRange(iter, fragmentation_config.getStopIndex()); // Create a fragmentation oligomer by constructing it with the // data that signal what oligomer it is, but for the moment // empty as far as the formula and the masses are concerned. OligomerSPtr oligomer1_sp = std::make_shared( mcsp_polymer, "NOT_SET", fragmentation_config.getName() /*fragmentationPathway.m_name*/, mcsp_polymer->modifiedMonomerCount( IndexRangeCollection(iter, fragmentation_config.getStopIndex())), Ionizer(), local_calc_options); // Now set the masses that were additively crafted along the iterations // in this loop: oligomer1_sp->setMasses(frag_chemistry_mono, frag_chemistry_avg); // Now set the formula of the oligomer: set its constructed-empty // formula to the formula that resulted from all the previous steps. oligomer1_sp->getFormulaRef().accountFormula( frag_chemistry_formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << frag_chemistry_formula.getActionFormula(); oligomer1_sp.reset(); return -1; } // qDebug() << "The oligomer1_sp before side chain contribution " // "has masses:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << oligomer1_sp->getFormulaCstRef().getActionFormula(); // At this moment, the new fragment might be challenged for the // fragmented monomer's side chain contribution. For example, in // nucleic acids, it happens that during a fragmentation, the // base of the fragmented monomer is decomposed and goes // away. This is implemented in massXpert with the ability to // tell the fragmenter that upon fragmentation the mass of the // monomer is to be removed. The skeleton mass is then added to // the formula of the fragmentation pattern. int monomer_contribution = fragmentation_config.getMonomerContribution(); if(monomer_contribution) { MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(iter); // First the masses. monomer_csp->accountMasses(oligomer1_sp->getMassRef(Enums::MassType::MONO), oligomer1_sp->getMassRef(Enums::MassType::AVG), monomer_contribution); bool ok = false; // Next the formula. oligomer1_sp->getFormulaRef().accountFormula( monomer_csp->getFormula(), isotopic_data_csp, monomer_contribution, ok); if(!ok) { qCritical() << "Failed to account formula:" << monomer_csp->getFormula(); oligomer1_sp.reset(); return -1; } // qDebug() << "The oligomer1_sp after side chain contribution " // "has masses:" // << "mono:" << oligomer1_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer1_sp->getMass(Enums::MassType::AVG) // << "updated formula:" // << oligomer1_sp->getFormulaRef().getActionFormula(); } // At this point we should check if the fragmentation // specification includes fragmentation rules that apply to this // fragment. for(const FragmentationRuleSPtr &fragmentation_rule_sp : fragmentation_config.getRulesCstRef()) { // The accounting of the fragmentationrule is performed on a // neutral oligomer, as defined by the fragmentation // formula. Later, we'll have to take into account the // fact that the user might want to calculate fragment m/z values // with z>1. // This round is not for real accounting, but only to check of the // currently iterated fragmentation rule should be accounted for (see // the true that indicates this call is only for checking). double mono; double avg; if(!accountFragmentationRule(fragmentation_rule_sp, /*only for checking*/ true, iter, Enums::FragEnd::RE, mono, avg)) continue; // This is why we first check above if the fragmentation rule was to // be accounted for: each fragmentationrule triggers the creation of a // new oligomer. OligomerSPtr oligomer2_sp = std::make_shared(*oligomer1_sp); // qDebug() << "The oligomer2_sp has been copied from " // "oligomer1_sp and " // "has masses:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << oligomer2_sp->getFormulaRef().getActionFormula(); accountFragmentationRule(fragmentation_rule_sp, false, iter, Enums::FragEnd::RE, oligomer2_sp->getMassRef(Enums::MassType::MONO), oligomer2_sp->getMassRef(Enums::MassType::AVG)); bool ok = false; oligomer2_sp->getFormulaRef().accountFormula( fragmentation_rule_sp->getFormulaCstRef().getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << fragmentation_rule_sp->getFormulaCstRef().getActionFormula(); oligomer1_sp.reset(); oligomer2_sp.reset(); return -1; } // qDebug() << "The oligomer2_sp after accounting for " // "fragmentation rule" // << fragmentation_rule_sp->getName() << "has masses:" // << "mono:" << oligomer2_sp->getMass(Enums::MassType::MONO) // << "avg:" << oligomer2_sp->getMass(Enums::MassType::AVG) // << "updated formula:" // << oligomer2_sp->getFormulaRef().getActionFormula(); // Let the following steps know that we actually succeeded // in preparing an oligonucleotide with a fragmentation // rule applied. frag_rule_applied = true; // At this point we have the fragment oligomer in a // neutral state (the fragmentation specification specifies a formula // to create a neutral fragmentation product). This is so that we can // later charge the ions as many times as requested by the user. // We still have to account for potential formulas set to the // FragmentationConfig, like when the user asks that for each product // ion -H2O or -NH3 be accounted for, // which happens all the time when doing peptide fragmentations. // These formulas are stored in the m_formulas member of // FragmentationConfig. // And once we have done this, we'll still have to actually charge the // fragments according to the FragmentationConfig's // [m_charge_level_start--m_stopIonizeLevel] range. // Each supplementary step above demultiplies the "versions" of the // fragment Oligomer currently created (oligomer2_sp). All the newly // created "versions" // will need to be stored in a container // (formula_variant_oligomers). So, for the moment copy the current // oligomer into a template oligomer that will be used as a template // to create all the "variant" oligomers. OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer2_sp); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2#(%3)") .arg(fragmentation_config.getName()) .arg(number + 1) .arg(fragmentation_rule_sp->getName()); int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); template_oligomer_for_formula_variants_sp->setName(name_with_charge); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "There are now" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers"; // We now have a container of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // qDebug() << "We now got" // << // ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // qDebug() << "We still have" // << // ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; // First off, we can finally delete the grand template oligomer. oligomer2_sp.reset(); if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; // qDebug() << "And now we have" // << m_oligomers.getOligomersCstRef().size() // << "oligomers in total"; count += transferred_count; } // End of // for(const FragmentationRuleSPtr &fragmentation_rule_sp : // fragmentation_config.getRulesCstRef()) // We are here because of two possible reasons: // 1. because the container of fragmentation_rule_sp was empty, in which // case we still have to validate and terminate the oligomer1 // (frag_rule_applied is false); // 2. because we finished dealing with the container of // fragmentation_rule_sp, in which case we ONLY add oligomer1 to the list // of fragments if none of the fragmentationrules analyzed above gave a // successfully generated fragment(frag_rule_applied is false). if(!frag_rule_applied) { // At this point we have a single fragment oligomer because we did not // had to apply any fragmentation rule, and thus have generated no // variant of it. We may have formulas to apply, as there might be // formulas in FragmentationConfig. // So, first create an oligomer with the "default" // fragmentation specification-driven neutral state (that // is, charge = 0). OligomerSPtr template_oligomer_for_formula_variants_sp = std::make_shared(*oligomer1_sp); // We can immediately set the name of template oligomer on which // to base the creation of the derivative formula-based // oligomers. QString name = QString("%1#%2") .arg(fragmentation_config.getName()) .arg(number + 1); int charge = 0; // Set the name of this template oligomer, but with the // charge in the form of "#z=1". QString name_with_charge = QString("%1#z=%2").arg(name).arg(charge); template_oligomer_for_formula_variants_sp->setName(name_with_charge); // We have not yet finished characterizing fully the oligomer, for // example, there might be formulas to be applied to the oligomer. // Also, the user might have asked for a number of charge states. To // store all these variants, create an oligomer container: OligomerCollection formula_variant_oligomers( fragmentation_config.getName(), mcsp_polymer); // Ask that this new container be filled-in with the variants obtained // by applying the Formula instances onto the template oligomer. // Indeed, the user may have asked in FragmentationConfig that // formulas like -H2O or -NH3 be accounted for // in the generation of the fragment oligomers. Account // for these potential formulas... // Note that we use std::move when passing // template_oligomer_for_formula_variants_sp to the function because // the function will move it as the first item in the // formula_variant_oligomers container. // int accountedFormulas = accountFormulas(std::move(template_oligomer_for_formula_variants_sp), formula_variant_oligomers, fragmentation_config, name, charge); // qDebug() << "There are now" // << formula_variant_oligomers.getOligomersCstRef().size() // << "formula variant oligomers"; // We now have a list of oligomers (or only one if there // was no formula to take into account). For each // oligomer, we have to account for the charge levels // asked by the user. OligomerCollection ionization_variant_oligomers = accountIonizationLevels(formula_variant_oligomers, fragmentation_config); // qDebug() << "We now got" // << // ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; // At this point we do not need the initial *uncharged* Oligomer // instances anymore. formula_variant_oligomers.clear(); // qDebug() << "We still have" // << // ionization_variant_oligomers.getOligomersCstRef().size() // << "ionization variant oligomers"; // First off, we can finally delete the grand template // oligomer (oligomer with no fragmentation rules applied). oligomer1_sp.reset(); if(!ionization_variant_oligomers.size()) { qCritical() << "Failed to generate ionized fragment oligomers."; return -1; } // Finally transfer all the oligomers generated for this fragmentation // to the container of ALL the oligomers. But before making // the transfer, compute the elemental composition and store it // as a property object. // Involves a std::move operation. std::size_t transferred_count = transferOligomers(ionization_variant_oligomers, m_oligomers); // qDebug() << "The number of transferred Oligomers:" // << transferred_count; // qDebug() << "And now we have" // << m_oligomers.getOligomersCstRef().size() // << "oligomers in total"; count += transferred_count; } // End of // if(!frag_rule_applied) else // (frag_rule_applied == true) { // There were fragmentation rule(s) that could be // successfully applied. Thus we already have created the // appropriate oligomers. Simply delete the template // oligomer. oligomer1_sp.reset(); } } // End of // for (int iter = fragmentation_config.getStopIndex(); // iter > fragmentation_config.getStopIndex() - 1; --iter, ++number) return count; } /*! \brief Accounts for the \a fragmentation_rule_sp FragmentationRule if the Monomer instance at \a monomer_index matches the requirement of the rule along with \a frag_end. The masses are accounted to \a mono and \a avg. If \a only_for_checking is true, then the computation is only a verification that the FragmentationRule is to be accounted for. If \a only_for_checking is false, the fragmentation rule is actually accounted for. Returns true if the FragmentationRule should be accounted for (if \a only_for_checking is true) or if it was effectively accounted for (if \a only_for_checking is false), false otherwise. */ bool Fragmenter::accountFragmentationRule( FragmentationRuleSPtr fragmentation_rule_sp, bool only_for_checking, std::size_t monomer_index, Enums::FragEnd frag_end, double &mono, double &avg) { MonomerSPtr prev_monomer_csp = 0; MonomerSPtr next_monomer_csp = 0; IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(fragmentation_rule_sp == nullptr || fragmentation_rule_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; if(monomer_index >= mcsp_polymer->size()) qFatalStream() << "Programming error. The index is out of bounds."; MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(monomer_index); if(!fragmentation_rule_sp->getCurrCode().isEmpty()) if(fragmentation_rule_sp->getCurrCode() != monomer_csp->getCode()) return false; if(!fragmentation_rule_sp->getPrevCode().isEmpty() && !fragmentation_rule_sp->getNextCode().isEmpty()) { // Use the overload (see globals.hpp) if(static_cast(frag_end & Enums::FragEnd::LE) || static_cast(frag_end & Enums::FragEnd::NE)) { if(!monomer_index) // There cannot be any getPrevCode since we are at monomer_index == // 0, at the first monomer_csp of the fragmentation // series. That means that we can return immediately. return false; // Since we know that we are either in LEFT or NONE end // mode, we know that previous is at monomer_index 'monomer_index' // - 1. Thus get the monomer_csp out of the sequence for this // monomer_index. prev_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index - 1); if(monomer_index == mcsp_polymer->size() - 1) // There cannot be any next code since we are already at // the last monomer_csp in the fragmentation series. return false; next_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index + 1); } else if(static_cast(frag_end & Enums::FragEnd::RE)) { if(!monomer_index) // There cannot be any getNextCode since getCurrCode is the last // monomer_csp in the fragmentation series. return false; next_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index - 1); if(monomer_index == mcsp_polymer->size() - 1) // There cannot be any previous code since getCurrCode is the // first in the fragmentation series. return false; prev_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index + 1); } else return false; // Now that the getPrevCode and getNextCode have been correctly // identified, we can go on and check if some conditions are // met. if(fragmentation_rule_sp->getPrevCode() == prev_monomer_csp->getCode() && fragmentation_rule_sp->getNextCode() == next_monomer_csp->getCode()) { if(only_for_checking) return true; // The fragmentation rule condition is met, we can apply its // formula. bool ok; Formula temp_formula(fragmentation_rule_sp->getFormulaCstRef()); temp_formula.accountMasses(ok, isotopic_data_csp, mono, avg, 1); if(!ok) { qCritical() << "Failed to account fragmentation rule"; return false; } return true; } else { if(only_for_checking) return false; else return true; } } // End of // if (!fragmentation_rule_sp->getPrevCode().isEmpty() && // !fragmentation_rule_sp->getNextCode().isEmpty()) else if(!fragmentation_rule_sp->getPrevCode().isEmpty()) { if(static_cast(frag_end & Enums::FragEnd::LE) || static_cast(frag_end & Enums::FragEnd::NE)) { if(!monomer_index) // There cannot be any getPrevCode since getCurrCode is already // the first of the fragmentation series. return false; // Since we know that fragEnd is either LEFT or NONE end, we // know what monomer_index has the getPrevCode: prev_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index - 1); } else if(static_cast(frag_end & Enums::FragEnd::RE)) { if(monomer_index == mcsp_polymer->size() - 1) // There cannot be any getPrevCode since getCurrCode is already // the first of the fragmentation series. return false; prev_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index + 1); } else return false; // Now that we have correctly identified the getPrevCode, we can go // on and check if some conditions are met. if(fragmentation_rule_sp->getPrevCode() == prev_monomer_csp->getCode()) { if(only_for_checking) return true; // The fragmentation rule condition is met, we can apply its // formula. bool ok; Formula temp_formula(fragmentation_rule_sp->getFormulaCstRef()); temp_formula.accountMasses(ok, isotopic_data_csp, mono, avg, 1); if(!ok) { qCritical() << "Failed to account fragmentation rule"; return false; } return true; } else { if(only_for_checking) return false; else return true; } } // End of // else if (!fragmentation_rule_sp->getPrevCode().isEmpty()) else if(!fragmentation_rule_sp->getNextCode().isEmpty()) { if(static_cast(frag_end & Enums::FragEnd::LE) || static_cast(frag_end & Enums::FragEnd::NE)) { if(monomer_index == mcsp_polymer->size() - 1) // There cannot be any getNextCode since getCurrCode is already // the last of the fragmentation series. return false; // Since we know that fragEnd is either LEFT or NONE end, we // know what monomer_index has the getPrevCode: next_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index + 1); } else if(static_cast(frag_end & Enums::FragEnd::RE)) { if(!monomer_index) // There cannot be any getPrevCode since getCurrCode is already // the last of the fragmentation series. return false; next_monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt( monomer_index - 1); } else return false; // Now that we have correctly identified the getNextCode, we can go // on and check if some conditions are met. if(fragmentation_rule_sp->getNextCode() == next_monomer_csp->getCode()) { if(only_for_checking) return true; // The fragmentation rule condition is met, we can apply its // formula. bool ok; Formula temp_formula(fragmentation_rule_sp->getFormulaCstRef()); temp_formula.accountMasses(ok, isotopic_data_csp, mono, avg, 1); if(!ok) { qCritical() << "Failed to account fragmentation rule"; return false; } return true; } else { if(only_for_checking) return false; else return true; } } // End of // else if (!fragmentation_rule_sp->getNextCode().isEmpty()) else { // All the prev and next codes are empty, which means that we // consider the conditions verified. if(only_for_checking) return true; bool ok; Formula temp_formula(fragmentation_rule_sp->getFormulaCstRef()); temp_formula.accountMasses(ok, isotopic_data_csp, mono, avg, 1); if(!ok) { qCritical() << "Failed to account fragmentation rule"; return false; } return true; } // We should never reach this point ! Q_ASSERT(0); return false; } /*! \brief Generates as many more Oligomer fragments are there are Formula instances to be accounted for. \a template_oligomer_sp is the Oligomer instance that serves as a template to generate as many variants as there are Formula instances in the \a fragmentation_config's member container of Formula instances. For example, if the caller wants to generate for each Oligomer frament a variant with -H2O and -NH3 formulas applied, then each fragment Oligomer in the \a oligomers collection will generate two variants: one with water-production decomposition and one with ammonia-producing decomposition. Thus, in the end, there will be as many times more fragment Oligomer instances in \a oligomers as there are Formula instances to be accounted for. \a name and \a charge are the name and charge onto which base the creation of the variant fragment Oligomer new name so that the fragment Oligomer has a name that documents the kind of fragmentation pathway is was generated from along with both the Formula that was accounted for and finally its charge. Returns the count of variant fragment Oligomer instances that were added into the \a oligomers collection. */ std::size_t Fragmenter::accountFormulas(OligomerSPtr &&template_oligomer_sp, OligomerCollection &oligomers, const FragmentationConfig &fragmentation_config, const QString &name, int charge) { // qDebug() << "Now accounting for formulas checked by the user (-NH3, -H20, " //"for example.)"; if(template_oligomer_sp == nullptr || template_oligomer_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); std::size_t count = 0; // The oligomer that we get as parameter is the // template on which to base the derivatives made on the basis of the // formulas. However, we must use it also as a mere fragmentation oligomer. // We get it as a pointer to be moved into the container as the very first // item in it. // qDebug() << "Template we got to base the formula variants on:" // << template_oligomer_sp->toString(); oligomers.getOligomersRef().push_back(std::move(template_oligomer_sp)); // template_oligomer_sp is now nullptr. We'll base the copies below // on the first item of the container. for(const Formula &formula : fragmentation_config.getFormulasCstRef()) { // We will apply the formula to a copy of the template oligomer OligomerSPtr formula_variant_oligomer_sp = std::make_shared(*oligomers.getOligomersRef().front()); // qDebug() << "Created new oligomer as a copy of the template:" // << formula_variant_oligomer_sp->toString(); // First, account the masses Formula temp_formula(formula); // qDebug() << "Accounting of fragmentation config's formula:" // << temp_formula.getActionFormula(); bool ok; temp_formula.accountMasses( ok, isotopic_data_csp, formula_variant_oligomer_sp->getMassRef(Enums::MassType::MONO), formula_variant_oligomer_sp->getMassRef(Enums::MassType::AVG)); if(!ok) { qCritical() << "Failed to account formula' masses:" << formula.getActionFormula(); formula_variant_oligomer_sp.reset(); continue; } // qDebug() << "Right after accounting the formula's masses" // << temp_formula.getActionFormula() << "the oligomer becomes:" // << formula_variant_oligomer_sp->toString(); // Second, account the formula. formula_variant_oligomer_sp->getFormulaRef().accountFormula( formula.getActionFormula(), isotopic_data_csp, 1, ok); if(!ok) { qCritical() << "Failed to account formula:" << formula.getActionFormula(); formula_variant_oligomer_sp.reset(); continue; } // qDebug() << "Right after accounting the formula proper" // << temp_formula.getActionFormula() << "the oligomer becomes:" // << formula_variant_oligomer_sp->toString(); // The new oligomer could be generated correctly. Append the // formula to its name, so that we'll be able to recognize it. QString name_with_formula = QString("%1#%2#z=%3") .arg(name) .arg(formula.getActionFormula()) .arg(charge); formula_variant_oligomer_sp->setName(name_with_formula); // At this point append the new oligomer to the list. // qDebug() << "Pushing back to oligomers, the new variant:" // << formula_variant_oligomer_sp->toString(); oligomers.getOligomersRef().push_back(formula_variant_oligomer_sp); ++count; } // qDebug() << "At the time of returning, the number of oligomers:" // << oligomers.getOligomersRef().size(); return count; } /*! \brief For each ionization level contained in the \a fragmentation_config start and stop ionization level members, create fragment Oligomer variants of all the Oligomer instances present in \a oligomers. The generated fragment Oligomer instances are stored in a OligomerCollection that is returned. */ OligomerCollection Fragmenter::accountIonizationLevels( OligomerCollection &oligomers, const FragmentationConfig &fragmentation_config) { bool failed = false; IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // qDebug() << "Handling the ionization levels for a container of" // << oligomers.getOligomersCstRef().size() << "oligomers"; // We get an Oligomer container (or only one, in fact, if no // -H2O/-NH3 formulas, for example, were asked for // (accountFormulas()), and we have for each one to compute the required // ionisation levels. Indeed, the user might ask for fragments // that are charged with different charge levels. Thus we need to create as // many new oligomers as needed for the different charge levels. Because the // ionization changes the values in the oligomer, and we need a new oligomer // each time, we duplicate the oligomer each time we need it. int ionization_level = fragmentation_config.getStartIonizeLevel(); int ionization_stop_level = fragmentation_config.getStopIonizeLevel(); // qDebug() << "Requested ionization level range:" // << "[" << ionization_level << "-" << ionization_stop_level << "]"; // We have to perform the operation for each oligomer in // oligomers. We populate a new Oligomers container that we will return // filled with at least the same Oligomer instances that were in // the Oligomer container passed as parameter. OligomerCollection charge_state_oligomers(fragmentation_config.getName(), mcsp_polymer); for(const OligomerSPtr &iter_oligomer_sp : oligomers.getOligomersRef()) { // The oligomer being iterated into is a non-ionized oligomer and // we will create as many ionized variants as there are ionization levels // requested. // qDebug() << "Now handling non-ionized oligomer with masses:" // << "mono:" << iter_oligomer_sp->getMass(Enums::MassType::MONO) // << "avg:" << iter_oligomer_sp->getMass(Enums::MassType::AVG) // << "and formula:" // << iter_oligomer_sp->getFormulaCstRef().getActionFormula(); // For this new oligomer, reset the initial ionization level. ionization_level = fragmentation_config.getStartIonizeLevel(); while(ionization_level <= ionization_stop_level) { // qDebug() << "Current ionization level:" << ionization_level; Ionizer temp_ionizer(m_ionizer); temp_ionizer.setLevel(ionization_level); // Make a copy of the non-ionized oligomer. OligomerSPtr new_oligomer_sp = std::make_shared(*iter_oligomer_sp); new_oligomer_sp->setIonizer(temp_ionizer); // If the result of the call below is Enums::IonizationOutcome::FAILED, then // that means that there was an error and we should return // immediately. If it is Enums::IonizationOutcome::UNCHANGED, then that means // that no error was encountered, but that no actual ionization took // place, so we need not take into account the oligomer. Enums::IonizationOutcome ionization_outcome = new_oligomer_sp->ionize(); if(ionization_outcome == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to ionize the oligomer."; new_oligomer_sp.reset(); failed = true; break; } else if(ionization_outcome == Enums::IonizationOutcome::UNCHANGED) { qInfo() << "The Oligomer ionization changed nothing."; new_oligomer_sp.reset(); continue; } // qDebug() << "After ionization with level:" // << ionization_level << ", oligomer has masses:" // << "mono:" << new_oligomer_sp->getMass(Enums::MassType::MONO) // << "avg:" << new_oligomer_sp->getMass(Enums::MassType::AVG); // Go on with effectively ionized oligomer variant. bool ok = false; new_oligomer_sp->getFormulaRef().accountFormula( temp_ionizer.getFormulaCstRef().getActionFormula(), isotopic_data_csp, temp_ionizer.getLevel(), ok); if(!ok) { qCritical() << "Failed to account the ionizer formula:" << temp_ionizer.getFormulaCstRef().getActionFormula(); new_oligomer_sp.reset(); continue; } // qDebug() << "After ionization with level:" // << ionization_level << ", oligomer has formula:" // << new_oligomer_sp->getFormulaCstRef().getActionFormula(); // At this point the ionization did indeed perform // something interesting, craft the name of the resulting // oligomer and set it. We must of the name of the // oligomer, but simply replace the value substring // "#z=xx" with "z=yy". QString name = new_oligomer_sp->getName(); QString charge_string = QString("z=%1").arg(temp_ionizer.charge()); name.replace(QRegularExpression("z=\\d+$"), charge_string); new_oligomer_sp->setName(name); // qDebug() << "After ionization, oligomer has name:" // << new_oligomer_sp->getName(); charge_state_oligomers.getOligomersRef().push_back(new_oligomer_sp); ++ionization_level; } // End of // while(ionization_level <= ionization_stop_level) // for (int iter = charge_level_start; iter < charge_level_stop; // ++iter) // If there was a single failure, we get here with failed // set to true. In that case, free the charge_state_oligomers and // return NULL. if(failed) { charge_state_oligomers.clear(); return charge_state_oligomers; } } // End of // for(const OligomerSPtr &iter_oligomer_sp : oligomers.getOligomersRef()) return charge_state_oligomers; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IndexRange.cpp000664 001750 001750 00000015616 15100504560 025024 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various indepstopent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IndexRange \inmodule libXpertMassCore \ingroup XpertMassCalculations \inheaderfile IndexRangeCollection.hpp \brief The IndexRange class provides a range of indices that delimit a region of interest in a \l{Polymer}'s \l{Sequence} instance. One major IndexRange use case is when defining for what sequence region of a Polymer the masses or the elemental composition is to be computed. Another use case is when performing Sequence cleavages or Oligomer fragmentations: the IndexRange enables the delimitation of the sequence of interest inside a given \l{Polymer}'s \l{Sequence} instance. When instantiating a new IndexRange, the start and stop values are automatically sorted in ascending order (start <= stop). \sa IndexRangeCollection, sortAscending(), sortDescending() */ /*! \variable MsXpS::libXpertMassCore::IndexRange::m_start \brief Index that marks the start of the index range. */ /*! \variable MsXpS::libXpertMassCore::IndexRange::m_stop \brief Index that marks the stop of the index range. */ /*! \brief Constructs an IndexRange instance using \a parent as the parent object of this new instance. Upon construction, the member data m_start and m_stop are initialized with std::numeric_limits::max() values as a means to check if the IndexRange was initialized by the user or not. */ IndexRange::IndexRange(QObject *parent): QObject(parent) { } /*! \ brief Constructs an IndexRange instance initializing the* start and stop members to the \a index_start and \a index_stop values, respectively. The values are sorted in ascending order upon construction. */ IndexRange::IndexRange(qsizetype index_start, qsizetype index_stop, QObject *parent) : QObject(parent), m_start(index_start), m_stop(index_stop) { sortAscending(); } /*! \brief Constructs an IndexRange instance using \a other as a template and \a parent as the parent of this new instance. */ IndexRange::IndexRange(const IndexRange &other, QObject *parent) : QObject(parent), m_start(other.m_start), m_stop(other.m_stop) { } /*! \ brief Destructs this IndexRange instance. */ IndexRange::~IndexRange() { } /*! \brief Initializes this instance using \a other. The parent of this instance is unchanged. */ void IndexRange::initialize(const IndexRange &other) { m_start = other.m_start; m_stop = other.m_stop; } /*! \brief Allocates an returns a new IndexRange instance, initializing it using this instance's member values and setting its parent to \a parent. */ IndexRange * IndexRange::clone(QObject *parent) { IndexRange *copy_p = new IndexRange(parent); copy_p->initialize(*this); return copy_p; } /*! \ br*ief Allocates an returns a new IndexRange instance, initializing it using \a other and setting its parent to \a parent. */ IndexRange * IndexRange::clone(const IndexRange &other, QObject *parent) { IndexRange *copy_p = new IndexRange(parent); copy_p->initialize(other); return copy_p; } /*! \brief Returns true if this and \a other IndexRange instances are identical. The parent is not checked. */ bool IndexRange::operator==(const IndexRange &other) const { return m_start == other.m_start && m_stop == other.m_stop; }; /*! \brief Returns true if this and \a other IndexRange instances are different. The parent is not checked. Return the negation of \l{operator==()}. */ bool IndexRange::operator!=(const IndexRange &other) const { return !operator==(other); } /*! \brief Makes sure that \l m_start is <= to \l m_stop. */ void IndexRange::sortAscending() { if(m_stop < m_start) std::swap(m_start, m_stop); } /*! \brief Makes sure that \l m_start is >= to \l m_stop. */ void IndexRange::sortDescending() { if(m_stop > m_start) std::swap(m_start, m_stop); } /*! \brief Returns true if this instance is valid. The IndexRange intance is considered valid if \l m_start and \l m_stop contain valid values, that is, not the default-assigned values. */ bool IndexRange::isValid() const { return m_start < std::numeric_limits::max() && m_stop < std::numeric_limits::max(); } /*! \brief Resets the member data to default values. */ void IndexRange::reset() { m_start = std::numeric_limits::max(); m_stop = std::numeric_limits::max(); } /*! \brief Returns a string representing this IndexRange instance in the form "[start-stop]". The start and stop values are indices (not positions). \sa positionsAsText() */ QString IndexRange::indicesAsText() const { return QString("[%1-%2]").arg(m_start).arg(m_stop); }; /*! \brief Returns a string representing this IndexRange instance in the form "[start-stop]". \note The start and stop values are positions (not indices). This means that the returned values correspond to \l m_start and \l m_stop both incremented by one unit. \sa indicesAsText() */ QString IndexRange::positionsAsText() const { return QString("[%1-%2]").arg(m_start + 1).arg(m_stop + 1); }; void IndexRange::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register IndexRange class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&IndexRange::staticMetaObject); engine->globalObject().setProperty("IndexRange", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IndexRangeCollection.cpp000664 001750 001750 00000055262 15100504560 027041 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various indepstopent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IndexRangeCollection \inmodule libXpertMassCore \ingroup XpertMassCalculations \inheaderfile IndexRangeCollection.hpp \brief The IndexRangeCollection class provides a collection of IndexRange instances that enable delimiting \l{Sequence} regions of interest in a given \l{Polymer} instance. \sa IndexRange */ /*! \variable MsXpS::libXpertMassCore::IndexRangeCollection::m_ranges \brief The container of \l{IndexRange} instances. */ /*! \variable MsXpS::libXpertMassCore::IndexRangeCollection::m_comment \brief A comment that can be associated to this IndexRangeCollection instance.. */ /*! \brief Constructs an empty IndexRangeCollection instance. */ IndexRangeCollection::IndexRangeCollection(QObject *parent): QObject(parent) { } /*! \brief Constructs an IndexRangeCollection instance with a single IndexRange object using \a index_start and \a index_stop. */ IndexRangeCollection::IndexRangeCollection(qsizetype index_start, qsizetype index_stop, QObject *parent) : QObject(parent) { IndexRange *index_range_p = new IndexRange(index_start, index_stop, this); m_ranges.append(index_range_p); } /*! \brief Constructs an IndexRangeCollection instance using \a index_ranges_string \sa parseIndexRanges() */ IndexRangeCollection::IndexRangeCollection(const QString &index_ranges_string, Enums::LocationType location_type, QObject *parent) { QList index_ranges = IndexRangeCollection::parseIndexRanges( index_ranges_string, location_type, parent); setIndexRanges(index_ranges); qDebug() << "With text" << index_ranges_string << "initialized this:" << indicesAsText(); } /*! \brief Constructs an IndexRangeCollection as a copy of \a other. */ IndexRangeCollection::IndexRangeCollection(const IndexRangeCollection &other, QObject *parent) : QObject(parent), m_comment(other.m_comment) { qDebug() << "Pseudo copy constructing IndexRangeCollection" << other.indicesAsText(); foreach(const IndexRange *item, other.m_ranges) m_ranges.append(new IndexRange(item->m_start, item->m_stop, this)); qDebug() << "After copying IndexRangeCollection, this:" << this->indicesAsText(); } /*! \brief Destructs this IndexRangeCollection instance. The \l IndexRangeCollection instances are freed. */ IndexRangeCollection::~IndexRangeCollection() { } /*! \brief Initializes this IndexRangeCollection instance using \a other and returns a reference to this instance. */ IndexRangeCollection & IndexRangeCollection::initialize(const IndexRangeCollection &other) { m_comment = other.m_comment; qDeleteAll(m_ranges); m_ranges.clear(); foreach(const IndexRange *item, m_ranges) m_ranges.append(new IndexRange(*item, this)); return *this; } /*! \brief Returns a newly allocated IndexRangeCollection instance with parent set to \a parent. */ IndexRangeCollection * IndexRangeCollection::clone(QObject *parent) { IndexRangeCollection *copy_p = new IndexRangeCollection(parent); foreach(const IndexRange *item, m_ranges) copy_p->m_ranges.append(new IndexRange(*item, copy_p)); return copy_p; } /*! \ b*rief Returns a newly allocated IndexRangeCollection instance initialized using \a other and with parent set to \a parent. */ IndexRangeCollection * IndexRangeCollection::clone(const IndexRangeCollection &other, QObject *parent) { IndexRangeCollection *copy_p = new IndexRangeCollection(parent); foreach(const IndexRange *item, other.m_ranges) copy_p->m_ranges.append(new IndexRange(*item, copy_p)); return copy_p; } /*! \brief Sets the comment to \a comment. */ void IndexRangeCollection::setComment(const QString &comment) { m_comment = comment; } /*! \brief Returns the comment. */ QString IndexRangeCollection::getComment() const { return m_comment; } /*! \brief Returns a const reference to the IndexRange container. */ const QList & IndexRangeCollection::getRangesCstRef() const { return m_ranges; } /*! \brief Returns a reference to the IndexRange container. */ QList & IndexRangeCollection::getRangesRef() { return m_ranges; } /*! \brief Parses \a index_ranges_string and returns a container with all the IndexRange entities parsed and parentship set to \a parent. If \a location_type is Enums::LocationType::POSITIION, the parsed values are decremented by one unit to convert from Position to Index. The values stored in the \c IndexRangeCollection are always indices. If \a location_type is Enums::LocationType::INDEX, the parsed values are stored as is. The \a index_ranges_string might contain one or more substrings in the format "[15-220]", like "[0-20][0-30][10-50][15-80][25-100][30-100]". If an error occurs, an empty container is returned. A sanity check is performed: the counts of '[' and of ']' must be the same. At the stop of the parse, the size of IndexRangeCollection must be same as the count of '['. If that check fails, an empty container is returned. */ QList IndexRangeCollection::parseIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type, QObject *parent) { qDebug() << "Parsing text:" << index_ranges_string; QString local_index_ranges_string = index_ranges_string; local_index_ranges_string = Utils::unspacify(local_index_ranges_string); // Sanity check qsizetype opening_brackets_count = local_index_ranges_string.count('['); qsizetype closing_brackets_count = local_index_ranges_string.count(']'); if(opening_brackets_count != closing_brackets_count) { qCritical() << "The string does not represent bona fide IndexRange " "descriptions."; return QList(); } QRegularExpression sub_regexp("\\[(\\d+)-(\\d+)\\]"); bool ok = false; QList parsed_index_ranges; for(const QRegularExpressionMatch &match : sub_regexp.globalMatch(local_index_ranges_string)) { QString sub_match = match.captured(0); qDebug() << "sub_match all captured" << sub_match; QString start_string = match.captured(1); QString stop_string = match.captured(2); int start = start_string.toInt(&ok); if(!ok) qFatalStream() << "Failed to parse the IndexRange instance(s)"; int stop = stop_string.toInt(&ok); if(!ok) qFatalStream() << "Failed to parse the IndexRange instance(s)"; if(location_type == Enums::LocationType::POSITION) { // Convert from input positions to local indices --start; --stop; } qDebug() << "start and stop:" << start << "and" << stop; // << "These values will be sorted in ascending order upon IndexRange // construction"; parsed_index_ranges.append(new IndexRange(start, stop, parent)); } // Sanity check if(parsed_index_ranges.size() != opening_brackets_count) { qWarning() << "The string does not represent bona fide IndexRange " "descriptions."; parsed_index_ranges.clear(); } qDebug() << "Could parse" << parsed_index_ranges.size() << "index ranges"; return parsed_index_ranges; } //////////////// OPERATORS ///////////////////// /*! \brief Returns true if this IndexRangeCollection instance is identical to \a other, false otherwise. */ bool IndexRangeCollection::operator==(const IndexRangeCollection &other) const { if(m_comment != other.m_comment) return false; if(m_ranges.size() != other.m_ranges.size()) return false; for(qsizetype iter = 0; iter < m_ranges.size(); ++iter) if(*m_ranges.at(iter) != *other.m_ranges.at(iter)) return false; return true; } /*! \brief Returns true if this IndexRangeCollection is different than \a other, false otherwise. This funtion returns the negated result of operator==(). */ bool IndexRangeCollection::operator!=(const IndexRangeCollection &other) const { return !operator==(other); } /*! \brief Clears this IndexRangeCollection's container of IndexRange instances and adds one IndexRange using \a start and \a stop. */ void IndexRangeCollection::setIndexRange(qsizetype start, qsizetype stop) { qDeleteAll(m_ranges); m_ranges.clear(); m_ranges.push_back(new IndexRange(start, stop, this)); } /*! \brief Clears this IndexRangeCollection's container of IndexRange instances and adds one \a index_range IndexRange. */ void IndexRangeCollection::setIndexRange(const IndexRange &index_range) { qDeleteAll(m_ranges); m_ranges.clear(); m_ranges.push_back(new IndexRange(index_range, this)); } /*! \brief Clears this IndexRangeCollection's container of IndexRange instances and adds the IndexRange instances contained in the \a index_ranges container. */ void IndexRangeCollection::setIndexRanges(const QList &index_ranges) { qDeleteAll(m_ranges); m_ranges.clear(); foreach(const IndexRange *item, index_ranges) m_ranges.append(new IndexRange(*item, this)); } /*! \brief Clears this IndexRangeCollection's container of IndexRange and adds the IndexRange instances contained in the \a index_ranges collection. */ void IndexRangeCollection::setIndexRanges(const IndexRangeCollection &index_ranges) { qDeleteAll(m_ranges); m_ranges.clear(); foreach(const IndexRange *item, index_ranges.m_ranges) m_ranges.append(new IndexRange(*item, this)); } /*! \brief Creates the IndexRange instances based on \a index_ranges_string and adds them to this IndexRange container. \note The container of IndexRange instances is first emptied. If there are not multiple regions, the format of the \a index_ranges_string is: \code "[228-246]" \endcode If there are multiple regions (for example when a cross-link exists), the format changes to account for the multiple regions: \code "[228-246][276-282][247-275]" \endcode \note It is expected that the values in the index_ranges_string are \e positions strings and not \e indices. Returns the count of added IndexRange instances or -1 if an error occurred. */ qsizetype IndexRangeCollection::setIndexRanges(const QString &index_ranges_string, Enums::LocationType location_type) { // We get a string in the form [xxx-yyy] (there can be more than // one such element, if cross-linked oligomers are calculated, like // "[228-246][276-282][247-275]". Because that string comes from // outside code, it is expected to contains positions // and not indices. So we have to decrement the start and stop // values by one. // qDebug() << "index_ranges_string:" << index_ranges_string; QList index_ranges = parseIndexRanges(index_ranges_string, location_type); setIndexRanges(index_ranges); return size(); } /*! \brief Adds one IndexRange using \a start and \a stop. */ void IndexRangeCollection::appendIndexRange(qsizetype start, qsizetype stop) { m_ranges.append(new IndexRange(start, stop, this)); } /*! \brief Adds one IndexRange as a copy of \a index_range. */ void IndexRangeCollection::appendIndexRange(const IndexRange &index_range) { m_ranges.append(new IndexRange(index_range, this)); } /*! \brief Adds IndexRange instances as copies of the instances in \a index_ranges. */ void IndexRangeCollection::appendIndexRanges(const QList &index_ranges) { foreach(const IndexRange *item, index_ranges) m_ranges.append(new IndexRange(*item, this)); } /*! \brief Adds IndexRange instances as copies of the instances in \a index_ranges. */ void IndexRangeCollection::appendIndexRanges( const IndexRangeCollection &index_ranges) { foreach(const IndexRange *item, index_ranges.m_ranges) m_ranges.append(new IndexRange(*item, this)); } //////////////// ACCESSING FUNCTIONS ///////////////////// /*! \brief Returns a const reference to the IndexRange at \a index. An index that is out of bounds is fatal. */ const IndexRange & IndexRangeCollection::getRangeCstRefAt(qsizetype index) const { if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return *m_ranges.at(index); } /*! \brief Returns reference to the IndexRange at \a index. An index that is out of bounds is fatal. */ IndexRange & IndexRangeCollection::getRangeRefAt(qsizetype index) { if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return *m_ranges.at(index); } /*! \brief Returns reference to the IndexRange at \a index. An index that is out of bounds is fatal. */ IndexRange * IndexRangeCollection::getRangeAt(qsizetype index) { if(index < 0 || index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_ranges.at(index); } /*! \brief Returns a constant iterator to the IndexRange at \a index. An index that is out of bounds is fatal. */ const QList::const_iterator IndexRangeCollection::getRangeCstIteratorAt(qsizetype index) const { if(index < 0 || index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_ranges.cbegin() + index; } /*! \brief Returns an iterator to the IndexRange at \a index. An index that is out of bounds is fatal. */ const QList::iterator IndexRangeCollection::getRangeIteratorAt(qsizetype index) { if(index < 0 || index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_ranges.begin() + index; } /*! \brief Returns the left-most start value throughout all the IndexRange instances. */ qsizetype IndexRangeCollection::leftMostIndexRangeStart() const { qDebug() << "IndexRangeCollection:" << this->indicesAsText(); qsizetype left_most_value = std::numeric_limits::max(); foreach(const IndexRange *item, m_ranges) { if(item->m_start < left_most_value) left_most_value = item->m_start; } return left_most_value; } /*! \brief Returns a container with the indices of the IndexRange instances that have the smallest start value. Searches all the IndexRange instances in this IndexRangeCollection instance that share the same IndexRange::start value that is actually the smallest such start value in the container. Each found IndexRange instance's index in this IndexRangeCollection instance is added to the container. \sa indicesOfRightMostIndexRanges(), isLeftMostIndexRange(), rightMostIndexRangeStop() */ QList IndexRangeCollection::indicesOfLeftMostIndexRanges() const { QList indices; if(!size()) return indices; qsizetype left_most_value = leftMostIndexRangeStart(); // At this point we know what's the leftmost index. We can use that // index to search for all the items that are also leftmost. for(qsizetype iter = 0; iter < m_ranges.size(); ++iter) { if(m_ranges.at(iter)->m_start == left_most_value) indices.push_back(iter); } return indices; } /*! \brief Returns true if \a index_range is the left-most IndexRange instance in this IndexRangeCollection instance, false otherwise. */ bool IndexRangeCollection::isLeftMostIndexRange(const IndexRange &index_range) const { // Is index_range the leftmost sequence range of *this // IndexRangeCollection instance ? qsizetype value = index_range.m_start; foreach(const IndexRange *item, m_ranges) { if(item->m_start < value) return false; } return true; } /*! \brief Return the right most stop ordinate throughout all the IndexRanges instances. */ qsizetype IndexRangeCollection::rightMostIndexRangeStop() const { qDebug() << "IndexRangeCollection:" << this->indicesAsText(); qsizetype rightMostValue = std::numeric_limits::min(); foreach(const IndexRange *item, m_ranges) { if(item->m_stop > rightMostValue) rightMostValue = item->m_stop; } return rightMostValue; } /*! \brief Returns a container with the indices of the IndexRange instances that have the greater stop value. Searches all the IndexRange instances in this IndexRangeCollection instance that share the same IndexRange::stop value that is actually the greatest such stopt value in the container. Each found IndexRange instance's index in this IndexRangeCollection instance is added to the container. \sa indicesOfLeftMostIndexRanges(), isLeftMostIndexRange(), isRightMostIndexRange() */ QList IndexRangeCollection::indicesOfRightMostIndexRanges() const { QList indices; if(!size()) return indices; qsizetype right_most_value = rightMostIndexRangeStop(); // At this point we now what's the rightmost index. We can use // that index to search for all the items that are also // rightmost. for(qsizetype iter = 0; iter < m_ranges.size(); ++iter) { if(m_ranges.at(iter)->m_stop == right_most_value) indices.push_back(iter); } return indices; } /*! \brief Returns true if \a index_range is the right-most IndexRange instance in this IndexRangeCollection instance, false otherwise. */ bool IndexRangeCollection::isRightMostIndexRange(const IndexRange &index_range) const { // Is index_range the rightmost sequence range of *this // IndexRangeCollection instance ? qsizetype value = index_range.m_stop; foreach(const IndexRange *item, m_ranges) { if(item->m_stop > value) return false; } return true; } /*! \brief Returns an \l IndexRange containing the leftmost start index and the rightmost stop index, effectively providing the most inclusive index range. */ IndexRange * IndexRangeCollection::mostInclusiveLeftRightIndexRange() const { return new IndexRange(leftMostIndexRangeStart(), rightMostIndexRangeStop(), const_cast(this)); } /*! \brief Returns true if \a index is found to be between the start and stop indices of at least one IndexRange instance in this IndexRangeCollection instance, false otherwise. If \a globally is set to true, then returns true if \a index is contained in the interval [smallest - greatest] indices throughout all the IndexRange instances. \sa leftMostIndexRangeStart(), rightMostIndexRangeStop() */ bool IndexRangeCollection::encompassIndex(qsizetype index, bool globally) const { if(globally) return (index >= leftMostIndexRangeStart() && index <= rightMostIndexRangeStop()); foreach(const IndexRange *item, m_ranges) { if(item->m_start <= index && item->m_stop >= index) return true; } return false; } /*! \brief Returns true if at least two IndexRange instances overlap. Two IndexRange instances overlap if the second's start value is less than the first's stop value and greater than the first's start value: |-----------------| |=================| or |-----------------| |=================| \sa encompassIndex() */ bool IndexRangeCollection::overlap() const { // Return true if there are overlapping regions in this // IndexRangeCollection instance. if(size() < 2) return false; foreach(const IndexRange *first_item, m_ranges) { qsizetype first_1 = first_item->m_start; qsizetype second_1 = first_item->m_stop; foreach(const IndexRange *second_item, m_ranges) { if(second_item == first_item) continue; qsizetype first_2 = second_item->m_start; if(first_2 <= second_1 && first_2 >= first_1) return true; } } return false; } /*! \brief Returns a string documenting the IndexRange instances in this IndexRanges instance. Each IndexRange instance is described like the following, with the values being the start and stop index values in the IndexRange: \code [156-350] \endcode \sa positionsAsText() */ QString IndexRangeCollection::indicesAsText() const { QString text; foreach(const IndexRange *item, m_ranges) { text += QString("[%1-%2]").arg(item->m_start).arg(item->m_stop); } return text; } /*! \brief Returns a string documenting the IndexRangeCollection in this IndexRanges. Each IndexRange instance is described like the following with the values being the start and stop position values in the IndexRange (start + 1, stop +1): \code [157-351] \endcode \note The values reported are not \e indices, but \e positions. \sa indicesAsText() */ QString IndexRangeCollection::positionsAsText() const { QString text; foreach(const IndexRange *item, m_ranges) { text += QString("[%1-%2]").arg(item->m_start + 1).arg(item->m_stop + 1); } return text; } /*! \brief Returns the size of the container of IndexRange instances. */ qsizetype IndexRangeCollection::size() const { return m_ranges.size(); } /*! \brief Clears all the members of this IndexRangeCollection instance. */ void IndexRangeCollection::clear() { m_comment = ""; m_ranges.clear(); } void IndexRangeCollection::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register IndexRangeCollection class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&IndexRangeCollection::staticMetaObject); engine->globalObject().setProperty("IndexRangeCollection", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Ionizer.cpp000664 001750 001750 00000130251 15100504560 024410 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" int ionizerMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::Ionizer"); int ionizerPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::IonizerPtr"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Ionizer \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Ionizer.hpp \brief The Ionizer class provides abstractions to ionize analytes. Ionizations are chemical reactions that bring a charge (or more charges) to an analyte. In this Ionizer class, that process is modelled along with the idea that a previously ionized analyte might be re-ionized with a different ionization chemistry (that is, either a different ionization agent, or a different ionization nominal charge or a different ionization level. To provide the basis for re-ionization, the class provides two ionization rules describing the ionization state (present and future) of the analyte managed by the Ionizer: \list \li one ionization rule (formula, nominal charge, level) for the "current state" of ionization; \li another ionization rule for the "ionization to come", that is the rule that will be applied to the analyte managed by this Ionizer when the ionize() function will be called. \endlist The class data members that describe the current ionization state of the analyte managed by the Ionizer have the "currentState" prefix to their name (eg \e m_currentStateFormula). The class data members that describe the so-called "ionization-to-come" ionization rule have no prefix associated to their name (eg \e m_formula). The ionization chemical reaction is described by the member \l Formula instances \l Ionizer::m_formula (ionization to come) and \l Ionizer::m_currentStateFormula (current state of ionization). The electric charge that is brought by this reaction is described by the members \l Ionizer::m_nominalCharge and \l Ionizer::m_currentStateNominalCharge. The ionization level is described by the members \l Ionizer::m_level and \l Ionizer::m_currentStateLevel. The ionization level is typically set to the number of ionization reactions, that is, for example, it would be set to 10 for a protein to be ionized ten times by protonation, each protonation event bringing a nominal charge of 1. An Ionizer like the following, if used to ionize a molecule of Mr 1000 \list \li Formula +H \li Charge 1 \li Level 1 \endlist would lead to an ion of mass 1001 and of m/z 1001. In protein chemistry, the ionization reaction is mainly a protonation reaction, which brings a single charge. Thus, the Formula would be "+H" and the charge 1. In MALDI, we would have a single protonation, thus level would be set to 1 by default. In electrospray ionization, more than one ionization reaction occur, and we could have a protein that is 25+, thus having an ionization level of 25, for example. An Ionizer like the following, if used to ionize a molecule of Mr 1000 \list \li Formula +H \li Charge 1 \li Level 4 \endlist would lead to an ion of mass 1004 and of m/z 251 (1004 / 4). An Ionizer like the following, if used to ionize a molecule of Mr 1000 \list \li Formula +Mg (in fact Mg2+) \li Charge 2 \li Level 4 \endlist would lead to an ion of mass 1000 + (24 * 4) and of m/z 137 = (1096 / 8). \note The real nature of the ionization is beared by the Formula (with, for example in protein chemistry, "+H" for protonation and, in nucleic acids chemistry, "-H" for deprotonation). Thus, an Ionizer is valid if it generates a m/z ratio after ionization of the analyte that is different than the previous (M) and if its member Formula validates successfully. This means that the following should be true: \list \li The Formula should be valid (that is, should contain at least one symbol (which might have a very small mass, like when the ionization is done by electron gain or loss); \li The charge is >0 (the ionization event should bring one charge, otherwise there is no ionization). To reset the ionization to 0 (that is to deionize the analyte, set the level to 0); \li The level is >= 0 (if the level is 0, then the analyte is considered not ionized); \endlist \note We do not consider compulsory that the Formula brings a mass difference whatsoever, because some ionizations might not involve heavy mass transfers, like electron gain or electron loss. However, the member Formula must validate successfully and that means that it cannot be empty. In that case, use a zero-sum formula like (-H+H) that has no weight. The Ionizer class should be used with caution because its internal state conditions heavily the reliability of the ionization calculations. The best way to proceed is to construct the ionizer fully at start and then only use the functional features of the class (ionize(), deionize(), molecularMasses()). */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::mcsp_isotopicData \brief The IsotopicData required to actually account for the ionization formula. \sa m_currentStateFormula, m_formula */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_formula \brief The Formula that is used to perform the ionization. For a protonation event, that would be "+H", for example. For a deprotonation event, that would be "-H". */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_currentStateFormula \brief The Formula that was last used to ionize the analyte This formula is only used when an analyte is being ionized. If this formula and the current state of ionization of the analyte is true, then it is used to first deionize the analyte. */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_nominalCharge \brief The charge that is brought to the molecule when it is ionized by the Ionizer with an ionization level (\l m_level) of 1. For a protonation, that would be \e 1. */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_currentStateNominalCharge \brief The charge that is currently bore by the analyte when it was last ionized. */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_level \brief The number of times this IonizRule is used to ionize a molecule. For example, applying this Ionizer to a molecule \list \li Formula +H \li Charge 1 \li Level 4 \endlist would protonate it 4 times, with a charge of 4 and an increment in mass of the mass of 4 times 1 proton. The m_level should be construed as "how many times should the ionization reaction (formula / nominal charge) be performed." */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_currentStateLevel \brief The number of times the "ionization-to-come" formula and nominal charge will be applied to the analyte managed by this Ionizer upon next call to \l ionize(). */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_isValid \brief Tells if this Ionizer is valid, both for the current state and for the ionization-to-come member data. \sa isValid(), validate() */ /*! \variable int MsXpS::libXpertMassCore::Ionizer::m_isCurrentStateValid \brief Tells if the current ionization state of the analyte managed by this Ionizer is valid. \sa isValid(), isCurrentStateValid(), validate(), validateCurrentState() */ /*! \brief Constructs an Ionizer initialized as an empty object. The instantiated Ionizer is invalid. */ Ionizer::Ionizer(QObject *parent): QObject(parent) { } /*! \brief Constructs an Ionizer initialized only with the IsotopicData. The instantiated Ionizer is invalid. */ Ionizer::Ionizer(IsotopicDataCstSPtr isotopic_data_csp, QObject *parent) : QObject(parent), mcsp_isotopicData(isotopic_data_csp) { } /*! \brief Constructs an Ionizer initialized with \a isotopic_data_csp, \a formula, \a charge, \a level. The current state member data are unitialized and the analyte managed by this Ionizer is thus in an un-ionized state. Instead, if ionize() is called, the ionization is performed using the data passed to this constructor. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. Note that the validation first checks the ionization-to-come members and then also checks the ionization current state members. \sa validate(), validateCurrentState() */ Ionizer::Ionizer(IsotopicDataCstSPtr isotopic_data_csp, const Formula &formula, int charge, int level, QObject *parent) : QObject(parent), mcsp_isotopicData(isotopic_data_csp), m_formula(formula), m_nominalCharge(charge), m_level(level) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The Ionizer just constructed is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs an Ionizer initialized with \a isotopic_data_csp, \a formula_string, \a charge, \a level. The current state member data are unitialized and the analyte managed by this Ionizer is thus in an un-ionized state. Instead, if ionize() is called, the ionization is performed using the data passed to this constructor. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. Note that the validation first checks the ionization-to-come members and then also checks the ionization current state members. \sa validate(), validateCurrentState() */ Ionizer::Ionizer(IsotopicDataCstSPtr isotopic_data_csp, const QString &formula_string, int charge, int level, QObject *parent) : QObject(parent), mcsp_isotopicData(isotopic_data_csp), m_formula(Formula(formula_string)), m_nominalCharge(charge), m_level(level) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The Ionizer just constructed is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs an Ionizer as a copy of \a other. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ Ionizer::Ionizer(const Ionizer &other, QObject *parent) : QObject(parent), mcsp_isotopicData(other.mcsp_isotopicData), m_formula(other.m_formula), m_nominalCharge(other.m_nominalCharge), m_level(other.m_level), m_currentStateFormula(other.m_currentStateFormula), m_currentStateNominalCharge(other.m_currentStateNominalCharge), m_currentStateLevel(other.m_currentStateLevel), m_isCurrentStateValid(other.m_isCurrentStateValid) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The Ionizer just constructed is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Initializes this Ionizer instance using \a other. */ void Ionizer::initialize(const Ionizer &other) { mcsp_isotopicData = other.mcsp_isotopicData; m_formula = other.m_formula; m_nominalCharge = other.m_nominalCharge; m_level = other.m_level; m_currentStateFormula = other.m_currentStateFormula; m_currentStateNominalCharge = other.m_currentStateNominalCharge; m_currentStateLevel = other.m_currentStateLevel; m_isCurrentStateValid = other.m_isCurrentStateValid; } /*! \brief Sets the IsotopicData to \a isotopic_data_csp. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Ionizer::setIsotopicDataCstSPtr(IsotopicDataCstSPtr isotopic_data_csp) { mcsp_isotopicData = isotopic_data_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "The Ionizer is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the IsotopicData. */ IsotopicDataCstSPtr Ionizer::getIsotopicDataCstSPtr() const { return mcsp_isotopicData; } /*! \brief Sets the Formula to \a formula. The m_formula describes the reaction to be applied to an analyte when the ionize() function gets called. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Ionizer::setFormula(const Formula &formula) { m_formula = formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Ionizer did not validate successfully, with errors:" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Sets the Formula to \a formula_string. The m_formula describes the reaction to be applied to an analyte when the ionize() function gets called. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Ionizer::setFormula(const QString &formula_string) { m_formula = Formula(formula_string); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Ionizer did not validate successfully, with errors:" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Returns a const reference to the Formula. */ const Formula & Ionizer::getFormulaCstRef() const { return m_formula; } /*! \brief Returns a reference to the Formula. */ Formula & Ionizer::getFormulaRef() { return m_formula; } /*! \brief Returns a const reference to the current state Formula. The m_currentStateFormula describes the formula that has been already applied to the analyte as it has already been ionized. */ const Formula & Ionizer::getCurrentStateFormulaCstRef() const { return m_currentStateFormula; } /*! \brief Sets the nominal charge to \a nominal_charge. The m_nominalCharge describes the charge that is brought to the analyte by the m_formula member when ionize() is called. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Ionizer::setNominalCharge(int nominal_charge) { m_nominalCharge = nominal_charge; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Ionizer did not validate successfully, with errors:" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Returns the nominal charge. */ int Ionizer::getNominalCharge() const { return m_nominalCharge; } /*! \brief Returns the current state nominal charge. The m_currentStateNominalCharge member datum describes the charge that was brought by the formula to the analyte when it was ionized (independently from the ionization level). */ int Ionizer::getCurrentStateNominalCharge() const { return m_currentStateNominalCharge; } /*! \brief Returns the total charge. The total charge is the product (m_nominalCharge * m_level). This charge will be brought to the analyte when ionize() gets called. */ int Ionizer::charge() const { return m_nominalCharge * m_level; } /*! \brief Returns the last status total charge. The total charge is the product (m_currentStateNominalCharge * m_currentStateLevel). This current state charge is the charge that was brought to the analyte when ionize() was called previously. It thus reflects the current charge of the analyte managed by this Ionizer. */ int Ionizer::currentStateCharge() const { return m_currentStateNominalCharge * m_currentStateLevel; } /*! \brief Sets the current state member data. This is useful when starting from an ionized state (for a Formula, for example) that will require to be ionized later in a different manner. See, for example, in MassXpert3, the MzCalculationDlg::getSrcIonizerData() function. The three parameters thus describe in full the current ionization status of the analyte managed by this Ionizer: \list \li \a formula The formula that describes the current ionization state; \li \a nominal_charge The charge that is brought by the reaction described in the formula; \li \a level The count of ionization events to be applied to the analyte managed by this Ionizer instance. \endlist The m_isCurrentStateValid is set to the result of the validation of the current state of this Ionizer. */ void Ionizer::forceCurrentState(const Formula &formula, int nominal_charge, int level) { m_currentStateFormula = formula; m_currentStateNominalCharge = nominal_charge; m_currentStateLevel = level; ErrorList error_list; m_isCurrentStateValid = validateCurrentState(&error_list); } /*! \brief Sets the current state ionization level to \a level. This function resets the current state ionization level and is practical to use for setting the analyte managed by this Ionizer to an un-ionized state even if the other members of the current state are valid (the formula and the nominal charged are correctly described). */ void Ionizer::forceCurrentStateLevel(int level) { m_currentStateLevel = level; } /*! \brief Sets the ionization level to \a level. This ionization level describes the number of times the ionization formula and the nominal charge need to be applied when ionize() gets called on the managed analyte. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Ionizer::setLevel(int level) { m_level = level; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Ionizer did not validate successfully, with errors:" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Returns the ionization level. This ionization level describes the number of times the ionization formula and the nominal charge need to be applied when ionize() gets called on the managed analyte. */ int Ionizer::getLevel() const { return m_level; } /*! \brief Returns the ionization level. This ionization level describes the number of times the ionization formula and the nominal charge have been applied when ionize() was called on the managed analyte. */ int Ionizer::getCurrentStateLevel() const { return m_currentStateLevel; } /*! \brief Allocates on the stack an Ionizer instance an initializes its ionization-to-come data using the current state data from this Ionizer. The current ionization state of the returned Ionizer instance are reset to nothing, effectively setting the managed analyte to a non-ionized state. Returns the allocated Ionizer instance. */ Ionizer Ionizer::makeIonizerWithCurrentStateData() { Ionizer ionizer(mcsp_isotopicData); ionizer.m_formula = m_currentStateFormula; ionizer.m_nominalCharge = m_currentStateNominalCharge; ionizer.m_level = m_currentStateLevel; ionizer.m_isValid = m_isCurrentStateValid; // All the current state of the new Ionizer are set to un-ionized, // which is good. return ionizer; } /*! \brief Returns the current ionization status. The analyte managed by this Ionizer is considered ionized if the current state nominal charge multiplied by the ionization level is not 0. \sa currentStateCharge() */ bool Ionizer::isIonized() const { return currentStateCharge() ? true : false; } /*! \brief Returns the outcome of the ionization process. The \a mono and \a avg masses are the masses of the analyte to be ionized. These masses are passed as non const because they are modified to become m/z values accounting for the ionization process (or to become Mr values accounting for the deionization process). The ionization is performed by accounting into \a mono and \a avg the formula of the ionization rule compounded by the level of ionization that is required. Then, the resulting masses are divided by the charge (that is the product of m_nominalCharge by m_level), thus becoming the m/z value of the ionized analyte. If this Ionizer instance's m_isIonized member value is true, the function does not do anything and returns Enums::IonizationOutcome::UNCHANGED. The modification of \a mono and \a avg is "atomic", that it, it occurs only if the ionization is successful and leads to values different than those initially passed to the function. */ Enums::IonizationOutcome Ionizer::ionize(double &mono, double &avg) const { if(!m_isValid) { qCritical() << "Cannot perform ionization with invalid Ionizer."; return Enums::IonizationOutcome::FAILED; } if(currentStateCharge()) { qInfo() << "Asking for ionization of an analyte that is already ionized. " "Performing deionization first."; Enums::IonizationOutcome ionization_outcome = deionize(mono, avg); if(ionization_outcome == Enums::IonizationOutcome::FAILED) qFatalStream() << "Failed to deionize."; } if(!charge()) { qInfo() << "Asking for ionization of an analyte with ionization charge 0. " "Performing no action."; return Enums::IonizationOutcome::UNCHANGED; } qDebug() << qSetRealNumberPrecision(10) << "Before ionization, Ionizer:" << toString() << "with entering masses: " << mono << "-" << avg << "\n\n"; double temp_mono = 0; double temp_avg = 0; // Account for the ionization rule formula in the temp masses. // Note the times(m_level) param to call below. bool ok = false; // To enforce constness of the function Formula temp_formula(m_formula); temp_formula.accountMasses( ok, mcsp_isotopicData, temp_mono, temp_avg, m_level); if(!ok) { qCritical() << "Failed to account the masses of the ionizer formula."; return Enums::IonizationOutcome::FAILED; } qDebug() << qSetRealNumberPrecision(10) << "The ionization formula:" << temp_formula.getActionFormula() << "resolved to computed masses:" << temp_mono << "-" << temp_avg; // Add the ionization mass to the the masses of the entity to be ionized. temp_mono += mono; temp_avg += avg; qDebug() << qSetRealNumberPrecision(10) << "The ionized analyte has masses:" << temp_mono << "-" << temp_avg; // Now account for the charge and thus produce m/z instead of m. temp_mono /= charge(); temp_avg /= charge(); qDebug() << qSetRealNumberPrecision(10) << "The ionized analyte has m/z:" << temp_mono << "-" << temp_avg; // Now establish if we did actually change something in the process: if((temp_mono != mono && temp_avg == avg) || (temp_mono == mono && temp_avg != avg)) qFatalStream() << "Programming error. Ionization failed."; if(temp_mono == mono && temp_avg == avg) qFatalStream() << "Programming error. Ionization failed."; // Yes, ionization changed something. mono = temp_mono; avg = temp_avg; m_currentStateFormula = m_formula; m_currentStateNominalCharge = m_nominalCharge; m_currentStateLevel = m_level; // m_wasIonized = true; m_isCurrentStateValid = true; qDebug() << qSetRealNumberPrecision(10) << "After ionization, " "with exiting masses: " << mono << "-" << avg << "\n\n"; return Enums::IonizationOutcome::IONIZED; } /*! \brief Attempts to deionize the analyte and returns the outcome. The analyte (of which the masses are passed as modifiable \a mono and \a avg arguments) is deionized using m_formula, m_nominalCharge and m_level. The following logic is applied: \list 1 \li If this Ionizable is not ionized, this function does not do anything and returns Enums::IonizationOutcome::UNCHANGED. \li If this Ionizer is not valid, this function returns Enums::IonizationOutcome::FAILED because it makes no sense to try to change the ionization of an analyte if the Ionizer is invalid. \li The deionization process is carried out. \endlist If the deionization process leads to masses different to \a mono and \a avg, then it is deemed successful and Enums::IonizationOutcome::DEIONIZED is returned, otherwise Enums::IonizationOutcome::UNCHANGED is returned. */ Enums::IonizationOutcome Ionizer::deionize(double &mono, double &avg) const { qDebug() << qSetRealNumberPrecision(10) << "Asking for deionization with ionizer:" << toString() << "and masses:" << mono << "-" << avg << "\n\n"; if(!m_isValid) { qCritical() << "Cannot perform ionization with invalid Ionizer."; return Enums::IonizationOutcome::FAILED; } if(!currentStateCharge()) { qInfo() << "Asking for deionization of an analyte that is not ionized. " "Performing no action."; return Enums::IonizationOutcome::UNCHANGED; } // Reverse the electrical effect of ionization using the last ionization // state. double temp_mono = mono * currentStateCharge(); double temp_avg = avg * currentStateCharge(); qDebug() << qSetRealNumberPrecision(10) << "After reversal of the by-the-charge division, new masses:" << temp_mono << "-" << temp_avg; // Use the last Formula to reverse the chemical effect of ionization. // Note the negated 'times'(- m_ionizeRule.level())param to call // below so that we revert the chemical action that led level // times to the ionization of the analyte. Note that we do not // have any compound(level * charge) because we are dealing with // matter here, not charges, thus only 'level' is to be taken into // consideration. bool ok = false; // To enforce constness of the function Formula temp_formula(m_currentStateFormula); temp_formula.accountMasses( ok, mcsp_isotopicData, temp_mono, temp_avg, -m_currentStateLevel); if(!ok) qFatalStream() << "Failed to account the masses of the ionizer formula."; qDebug() << qSetRealNumberPrecision(10) << "After having removed the ionization last formula masses from the " "previously computed (M+z) mono and avg masses: we now get Mr masses:" << temp_mono << "-" << temp_avg; // Now establish if we did actually change something in the process: if((temp_mono != mono && temp_avg == avg) || (temp_mono == mono && temp_avg != avg) || (temp_mono == mono && temp_avg == avg)) qFatalStream() << "The deionization failed."; // Yes, deionization changed something. mono = temp_mono; avg = temp_avg; m_currentStateFormula.clear(); m_currentStateNominalCharge = 0; m_currentStateLevel = 0; m_isCurrentStateValid = true; return Enums::IonizationOutcome::DEIONIZED; } /*! \brief Sets the molecular mass of the analyte of which the masses are passed as modifiable parameters in \a mono and \a avg. The analyte is first deionized (on copy data) and if the deionization process was successful, \a mono and \a avg are set to the masses of the deionized analyte (that is, Mr molecular masses). If the analyte is not ionized, returns Enums::IonizationOutcome::UNCHANGED (and nothing is changed). If the analyte was actually ionized, then if it is first successfully deionized, then the analyte is set to deionized and the \a mono and \a avg masses are set to the Mr molecular mass. */ Enums::IonizationOutcome Ionizer::molecularMasses(double &mono, double &avg) const { qDebug() << "Asking for computation of molecular masses with ionizer" << toString() << "and masses" << mono << "-" << avg; if(!currentStateCharge()) { qInfo() << "Asking for molecular masses of a non-ionized analyte. " "Leaving them unchanged."; return Enums::IonizationOutcome::UNCHANGED; } double temp_mono = mono; double temp_avg = avg; Enums::IonizationOutcome outcome = deionize(temp_mono, temp_avg); if(outcome == Enums::IonizationOutcome::FAILED) qFatalStream() << "Programming error. Failed to deionize."; if(outcome == Enums::IonizationOutcome::DEIONIZED) { mono = temp_mono; avg = temp_avg; m_currentStateFormula = m_formula; m_currentStateNominalCharge = m_nominalCharge; m_currentStateLevel = 0; m_isCurrentStateValid = true; } return outcome; } /*! \brief Assigns \a other to this Ionizer. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. Returns a reference to this ionization rule. */ Ionizer & Ionizer::operator=(const Ionizer &other) { if(&other == this) return *this; mcsp_isotopicData = other.mcsp_isotopicData; m_formula = other.m_formula; m_nominalCharge = other.m_nominalCharge; m_level = other.m_level; m_isValid = other.m_isValid; m_currentStateFormula = other.m_currentStateFormula; m_currentStateNominalCharge = other.m_currentStateNominalCharge; m_currentStateLevel = other.m_currentStateLevel; m_isCurrentStateValid = other.m_isCurrentStateValid; return *this; } /*! \brief Returns true if this Ionizer is identical to \a other, false otherwise. \note The comparison of the IsotopicData is deep, with a true comparison of all the Isotope instances, not only of the shared pointers. */ bool Ionizer::operator==(const Ionizer &other) const { if(&other == this) return true; if(mcsp_isotopicData != nullptr && other.mcsp_isotopicData != nullptr) { qDebug() << "Now checking the isotopic data"; if(*mcsp_isotopicData.get() != *other.mcsp_isotopicData.get()) { qDebug() << "The isotopic data are not identical."; return false; } } if(m_formula != other.m_formula) { qDebug() << "The formulas are different."; return false; } if(m_nominalCharge != other.m_nominalCharge) { qDebug() << "The nominal charges are different."; return false; } if(m_level != other.m_level) { qDebug() << "The levels are different."; return false; } if(m_isValid != other.m_isValid) { qDebug() << "The validity status are different."; return false; } if(m_currentStateFormula != other.m_currentStateFormula) { qDebug() << "The current formulas are different."; return false; } if(m_currentStateNominalCharge != other.m_currentStateNominalCharge) { qDebug() << "The current nominal charges are different."; return false; } if(m_currentStateLevel != other.m_currentStateLevel) { qDebug() << "The current levels are different."; return false; } if(m_isCurrentStateValid != other.m_isCurrentStateValid) { qDebug() << "The current validity statuses are different."; return false; } return true; } /*! \brief Returns true if this Ionizer is different than \a other, false otherwise. Returns the negated result of operator==(). */ bool Ionizer::operator!=(const Ionizer &other) const { if(&other == this) return false; return !operator==(other); } /*! \brief Renders the ionization rule XML \a element. The XML element is parsed and the data extracted from the XML data are set to this Ionizer instance. The DTD says this: A typical ionization rule element looks like this: \code +H 1 1 \endcode The caller is reponsible for checking the validity of the IonizeRule prior use. Returns true if the parsing is successful, false otherwise. \sa formatXmlIonizeRuleElement(int offset, const QString &indent) */ bool Ionizer::renderXmlIonizeRuleElement(const QDomElement &element) { // Assume the Ionizer is valid. We'll change that as we go. m_isValid = true; QDomElement child; // // +H // 1 // 1 // if(element.tagName() != "ionizerule") { qCritical() << "Cannot render ionization rule. Problem with the " " element."; m_isValid = false; return m_isValid; } // child = element.firstChildElement(); if(child.tagName() != "formula") { qCritical() << "Cannot render ionization rule. Problem with the " " element."; m_isValid = false; return m_isValid; } if(!m_formula.renderXmlFormulaElement(child)) { qCritical() << "Cannot render ionization rule. Problem with the validation" "of the Formula."; m_isValid = false; } qDebug() << "Rendered formula:" << m_formula.getActionFormula(); // child = child.nextSiblingElement(); if(child.tagName() != "charge") { qCritical() << "Cannot render ionization rule. Problem with the " " element."; m_isValid = false; return m_isValid; } bool ok = false; m_nominalCharge = child.text().toInt(&ok); if(!m_nominalCharge && !ok) { qCritical() << "Cannot render ionization rule. Problem with the " "nominal charge value."; m_isValid = false; } qDebug() << "Rendered nominal charge:" << m_nominalCharge; // child = child.nextSiblingElement(); if(child.tagName() != "level") { qCritical() << "Cannot render ionization rule. Problem with the " " element."; m_isValid = false; return m_isValid; } ok = false; m_level = child.text().toInt(&ok); if(!m_level && !ok) { qCritical() << "Cannot render ionization rule. Problem with the " "level value."; m_isValid = false; } qDebug() << "Rendered level:" << m_level; qDebug() << "At this point validate the Ionizer."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to successfully render with errors:" << Utils::joinErrorList(error_list, ", "); return m_isValid; } /*! \brief Formats and returns a string suitable to use as an XML element. Formats a string suitable to be used as an XML element in a polymer chemistry definition file. The typical ionization rule element that is generated in this function looks like this: The DTD says this: A typical ionization rule element looks like this: \code ~~+H ~~1 ~~1 \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. \sa renderXmlIonizeRuleElement(const QDomElement &element) */ QString Ionizer::formatXmlIonizeRuleElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: * * * +H * 1 * 1 * * */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n") .arg(lead) .arg(m_formula.getActionFormula(/*with_title*/ true)); text += QString("%1%2\n").arg(lead).arg(m_nominalCharge); text += QString("%1%2\n").arg(lead).arg(m_level); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } /*! \brief Validates this Ionizer, setting any error message to \a error_list_p. An Ionizer is valid if the following data for the ionization-to-come: \list \li The member IsotopicData are available \li The Formula validates successfully (that is, should contain at least one atom (for a zero-mass formula, use "+H-H", for example); \li The nominal charge should be != 0 because otherwise the Ionizer raison d'être is absent. \endlist Further, the current ionization state data are checked and are valid if: \list \li The member IsotopicData are available \li The Formula validates successfully if it is not empty (that is, should contain at least one atom (for a zero-mass formula, use "+H-H", for example); \endlist Note that both the current state nominal charge and level may be 0 (which is logical if the analyte being managed by this Ionizer is not currently ionized). If any of the tests above fail, the Ionizer is considered invalid and the validity status (m_isValid) value is set to false; true otherwise. Returns true if validation succeeded, false otherwise. \sa Formula::validate(), validateCurrentState(), isValid() */ bool Ionizer::validate(ErrorList *error_list_p) const { Q_ASSERT(error_list_p != nullptr); qsizetype error_count = error_list_p->size(); if(mcsp_isotopicData == nullptr || mcsp_isotopicData.get() == nullptr || !mcsp_isotopicData->size()) { qCritical() << "Cannot validate Ionizer without available IsotopicData."; error_list_p->push_back( "Cannot validate Ionizer without available IsotopicData"); m_isValid = false; return m_isValid; } // qDebug() << "Going to validate the formula" << // m_formula.getActionFormula(); if(!m_formula.validate(mcsp_isotopicData, error_list_p)) { QString msg = QString("The ionizer formula %1 failed to validate successfully") .arg(m_formula.getActionFormula()); qCritical() << msg; error_list_p->push_back(msg); } // Note that it is perfectly possible that the level of the ionization in // the ionizer be 0. Instead, having the nominal charge set to 0 means // that the ionizer will never ionize anything even by setting the level // to some non-naught value. qDebug() << "Going to validate the nominal charge:" << m_nominalCharge; if(!m_nominalCharge) { qCritical() << "Cannot validate Ionizer that has its nominal charge equal to 0."; error_list_p->push_back( "Cannot validate Ionizer that has its nominal charge equal to 0"); } // And now validate the current state // qDebug() << "Going to validate the current state data."; validateCurrentState(error_list_p); // qDebug() << "Done validating the current state data with status:" << ok; // If we added errors, then that means that the Ionizer was not valid. m_isValid = (error_list_p->size() > error_count ? false : true); if(!m_isValid) qDebug() << "Failed to validate this Ionizer, with errors:" << Utils::joinErrorList(*error_list_p); return m_isValid; } /*! \brief Validates this Ionizer current state, setting any error message to \a error_list_p. An Ionizer has a valid current state if: \list \li The member IsotopicData are available \li The Formula validates successfully if it is not empty (that is, should contain at least one atom (for a zero-mass formula, use "+H-H", for example); \endlist Note that both the current state nominal charge and level may be 0 (which is logical if the analyte being managed by this Ionizer is not currently ionized). If any of the tests above fail, the Ionizer is considered as invalid for the current ionization state and the validity status (m_isCurrentStateValid) value is set to false; true otherwise. Returns true if validation succeeded, false otherwise. \sa Formula::validate(), validate(), isValid() */ bool Ionizer::validateCurrentState(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_isotopicData == nullptr || mcsp_isotopicData.get() == nullptr || !mcsp_isotopicData->size()) { qCritical() << "Cannot validate Ionizer without available IsotopicData."; error_list_p->push_back( "Cannot validate Ionizer without available IsotopicData"); m_isCurrentStateValid = false; return m_isCurrentStateValid; } // The current state formula might be empty if the analyte state // described the the current ionization state is not charged. if(!m_currentStateFormula.getActionFormula().isEmpty()) { qDebug() << "Going to validate the current state formula:" << m_currentStateFormula.getActionFormula(); if(!m_currentStateFormula.validate(mcsp_isotopicData, error_list_p)) { QString msg = QString( "The ionizer current state formula %1 failed to " "validate successfully") .arg(m_currentStateFormula.getActionFormula()); qCritical() << msg; error_list_p->push_back(msg); } } // The current state nominal charge and the current state level might be // 0 because the user might want to set up the Ionizer in such a way // that the analyte is not charged at all. // If we added errors, then that means that the Monomer was not valid. m_isCurrentStateValid = (error_list_p->size() > error_count ? false : true); if(!m_isCurrentStateValid) qDebug() << "Failed to validate this Ionizer current state, with errors:" << Utils::joinErrorList(*error_list_p); return m_isCurrentStateValid; } /*! \brief Returns the validity status of this instance: true if this Ionizer is valid, false otherwise. \sa validateCurrentState(), validate() */ bool Ionizer::isValid() const { return m_isValid; } /*! \brief Returns the validity status of the current state of this Ionizer instance: true if the current state is valid, false otherwise. \sa validate() */ bool Ionizer::isCurrentStateValid() const { return m_isCurrentStateValid; } //////////////// UTILS ///////////////////// /*! \brief Returns a string holding a textual representation of the member data. If \a with_title is true, the member formulas (m_formula and m_currentStateFormula) are output with their title (if any). */ QString Ionizer::toString(bool with_title) const { QString text; text += QString( "Ionizer destination state. formula: %1 - nominal charge: %2 - level: " "%3 - is valid: %4\n") .arg(m_formula.getActionFormula(with_title)) .arg(m_nominalCharge) .arg(m_level) .arg(m_isValid); text += QString( "Ionizer current state. formula: %1 - nominal charge: %2 - level: %3 " "- charge: %4 - was valid: %5\n") .arg(m_currentStateFormula.getActionFormula(with_title)) .arg(m_currentStateNominalCharge) .arg(m_currentStateLevel) .arg(currentStateCharge()) .arg(m_isCurrentStateValid); return text; } /*! \brief Resets this instance to default values. */ void Ionizer::clear() { mcsp_isotopicData.reset(); m_formula.clear(); m_currentStateFormula.clear(); m_nominalCharge = 0; m_currentStateNominalCharge = 0; m_level = 0; m_currentStateLevel = 0; m_isValid = false; m_isCurrentStateValid = false; } void Ionizer::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register Ionizer class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&Ionizer::staticMetaObject); engine->globalObject().setProperty("Ionizer", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Isotope.cpp000664 001750 001750 00000063551 15100504560 024423 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2024 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" int isotopeMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::Isotope"); int isotopePtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::IsotopePtr"); int isotopeSPtrMetaTypeId = qRegisterMetaType>( "QSharedPointer"); int isotopeCstSPtrMetaTypeId = qRegisterMetaType>( "QSharedPointer"); namespace MsXpS { namespace libXpertMassCore { // #include // // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // This is the order of the columns in the gui TableView // ELEMENT, // SYMBOL, // MASS, // PROBABILITY /*! \enum MsXpS::libXpertMassCore::IsotopeFields \brief This enum type documents the various member data in \l{Isotope}. The values assigned to the various enum members are used to specify the columsn in the GUI table view. They are also used to access substrings in the proper order in the overloaded initialize() function. \value NAME Indicates Isotope::m_name. \value SYMBOL Indicates the Isotope::m_symbol. \value MASS Indicates the Isotope::m_mass. \value PROBABILITY Indicates the Isotope::m_probability. \omitvalue LAST */ /*! \class MsXpS::libXpertMassCore::Isotope \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Isotope.hpp \brief The Isotope class models an isotope. The Isotope class models an Isotope by featuring all the methods and member data required to fully characterize an isotope. The member data in this class have been inspired by the element tables from the IsoSpec library. Please, see \l{https://github.com/MatteoLacki/IsoSpec/}. */ /*! \variable MsXpS::libXpertMassCore::Isotope::m_name \brief The element name, like "carbon" or "nitrogen" (lowercase). */ /*! \variable MsXpS::libXpertMassCore::Isotope::m_symbol \brief The element symbol, like "C" or "N". */ /*! \variable MsXpS::libXpertMassCore::Isotope::m_mass \brief The mass of this isotope. Cannot be negative. */ /*! \variable MsXpS::libXpertMassCore::Isotope::m_probability \brief The probability of this isotope, that is, its abundance. Cannot be negative nor superior to 1. */ /*! \variable MsXpS::libXpertMassCore::Isotope::m_isValid \brief The validity status of this isotope. \sa validate() */ /*! \typedef MsXpS::libXpertMassCore::IsotopeQSPtr \relates Isotope Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::IsotopeCstQSPtr \relates Isotope Synonym for std::shared_ptr. */ /*! \brief Constructs an absolutely empty \l{Isotope}. The Isotope instance is invalid. It can be later intialized with the setter functions or the initialization functions. \sa initialize(const QString &), initialize(const QString &name, const QString &symbol, double mass, double probability) */ Isotope::Isotope(QObject *parent): QObject(parent) { } /*! \overload \brief Constructs the \l{Isotope} with all the required arguments. The isotope is created as a fully documented instance if all the following parameters are correctly set: \list \li \a name (\l{m_name}): element's name \li \a symbol (\l{m_symbol}): element's symbol \li \a mass (\l{m_mass}): isotope's mass \li \a probability (\l{m_probability}): isotope's probability, that is abundance \endlist \sa initialize(const QString &name, const QString &symbol, double mass, double probability) */ Isotope::Isotope(const QString &name, const QString &symbol, double mass, double probability, QObject *parent) : QObject(parent), m_name(name), m_symbol(symbol), m_mass(mass), m_probability(probability) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { QString errors = Utils::joinErrorList(error_list, "\n"); qWarning().noquote() << "The constructed isotope is not valid, with errors:\n" << errors; } } /*! \overload \brief Constructs the \l{Isotope} as a copy of \a other setting parent to \a parent. */ Isotope::Isotope(const Isotope &other, QObject *parent): QObject(parent) { qDebug() << "Constructing using reference."; m_name = other.m_name; m_symbol = other.m_symbol; m_mass = other.m_mass; m_probability = other.m_probability; QVector error_list; m_isValid = validate(&error_list); if(!m_isValid) { QString errors = Utils::joinErrorList(error_list, "\n"); qWarning().noquote() << "The copy-constructed isotope is not valid, with errors:\n" << errors; } } /*! \overload \brief Constructs the \l{Isotope} as a copy of \a other_p, setting parent to \a parent. */ Isotope::Isotope(const Isotope *other_p, QObject *parent): QObject(parent) { Q_ASSERT(other_p != nullptr); qDebug() << "Constructing using pointer."; m_name = other_p->m_name; m_symbol = other_p->m_symbol; m_mass = other_p->m_mass; m_probability = other_p->m_probability; QVector error_list; m_isValid = validate(&error_list); if(!m_isValid) { QString errors = Utils::joinErrorList(error_list, "\n"); qWarning().noquote() << "The copy-constructed isotope is not valid, with errors:\n" << errors; } } /*! \overload \brief Isotope::Isotope(const QString &text, QObject *parent) Constructs this \l{Isotope} using all the data in the \a text string, setting parent to \a parent. The string contains all the Isotope data, separated by a comma ',' exactly with the same format as that implemented by Isotope::toString(). \sa initialize(const QString &), initialize(const QString &name, const QString &symbol, double mass, double probability), toString() */ Isotope::Isotope(const QString &text, QObject *parent): QObject(parent) { // This is somehow the reverse of toString(). if(!initialize(text)) qWarning() << "The initialization failed using text:" << text; } /*! \brief Destructs this Isotope. */ Isotope::~Isotope() { } /*! \brief Returns a heap-allocated Isotope as a copy of \c this Isotope but reparented with \a parent. Each member datum in \c this Isotope is copied the returned Isotope, effectivaly making a copy of \c this Isotope and returning it after reparenting to \a parent. */ Isotope * Isotope::clone(QObject *parent) const { Isotope *copy_p = new Isotope(parent); copy_p->initialize(m_name, m_symbol, m_mass, m_probability); return copy_p; } /*! \brief Returns a heap-allocated Isotope as a copy of \a other but reparented with \a parent. Each member datum in \a other is copied the returned Isotope, effectivaly making a copy of \a other and returning it after reparenting to \a parent. */ Isotope * Isotope::clone(const Isotope &other, QObject *parent) { Isotope *copy_p = new Isotope(parent); copy_p->initialize( other.m_name, other.m_symbol, other.m_mass, other.m_probability); return copy_p; } /*! \brief Resets all the member data to default invalid values. The m_isValid member datum is set to false. */ void Isotope::clear() { m_name = ""; m_symbol = ""; m_mass = -1.0; m_probability = -1.0; m_isValid = false; } /*! \overload MsXpS::libXpertMassCore::initialize(const QString &name, const QString &symbol, double mass, double probability) \brief Initializes this Isotope instance using \a name, \a symbol, \a mass and \a probability. Returns true if initilization was without error, false otherwise. \sa initialize(const QString &text) */ bool Isotope::initialize(const QString &name, const QString &symbol, double mass, double probability) { // Start by resetting this Isotope instance (sets m_isValid to false) clear(); QString local_text; QRegularExpression regexp; //////// The element's name local_text = name.simplified(); regexp.setPattern("^[a-z]+$"); if(!regexp.match(local_text).hasMatch()) { qWarning() << "Failed to initialize the element's name."; } else m_name = local_text; //////// The element's symbol local_text = symbol.simplified(); regexp.setPattern("^[A-Z][a-z]*$"); if(!regexp.match(local_text).hasMatch()) qWarning() << "Failed to initialize the element's symbol."; else m_symbol = local_text; //////// The isotope's mass if(mass < 0) qWarning() << "Failed to initialize the isotope's mass."; else m_mass = mass; //////// The isotope's probability if(probability < 0 || probability > 1) qWarning() << "Failed to initialize the isotope's probability."; else m_probability = probability; // At this point, it looks like the Isotope is syntactically valid, // but is it from a chemical standpoint? QVector error_list; m_isValid = validate(&error_list); if(!m_isValid) { QString errors; for(const QString &error : error_list) errors.append(QString("%1\n").arg(error)); qWarning().noquote() << "The initialized isotope is not valid, with errors:" << errors; } return m_isValid; } /*! \brief Initializes the \l{Isotope} using all the data in the \a text string. The string passed as argument is QString::simplified() and QString::split() with ',' as the delimiter. The obtained strings are converted to the corresponding numerical or textual values to initalize all the member data. Returns true if the string contained valid substrings that successfully initialized the \l{Isotope}, false otherwise. \sa Isotope(const QString &text, QObject *parent), initialize(const QString &name, const QString &symbol, double mass, double probability) */ bool Isotope::initialize(const QString &text) { qDebug() << "Initializing Isotope with text:" << text; // Start by resetting this Isotope instance (sets m_isValid to false) clear(); if(text.isEmpty()) { qCritical() << "The text used to initialize the Isotope instance is empty."; m_isValid = false; return m_isValid; } // At this point deconstruct the line. Make sure we remove all spaces from // beginning and end AND reduce all multiple-space substrings in the text to // only single-space substrings. QString local_text = text.simplified(); qDebug() << "After simplification, the text becomes:" << local_text; QStringList string_list = local_text.split(','); qDebug() << "After splitting text at commas, count of the subtext strings:" << string_list.size(); // We may be reading data from a line that comes from an old version of // isotopic data files that contain much more information than from newer // version of such files. Check this. if(string_list.size() == 10) { // We may be reading from an older version file. return initializeVersion1(text); } else if(string_list.size() == static_cast(IsotopeFields::LAST)) { // We may be reading from a more recent version file. return initializeVersion2(text); } else { return false; } return false; } /*! \brief Initializes the \l{Isotope} using all the data in the \a text string. The string passed as argument is QString::simplified() and QString::split() with ',' as the delimiter. The index of the substring in the string list obtained by splitting the initial text at ',' delimiter defines what data element it corresponds to. The obtained strings are converted to the corresponding numerical or textual values to initalize all the member data. Returns true if the string contained valid substrings that successfully initialized the \l{Isotope}, false otherwise. \sa Isotope(const QString &text, QObject *parent), initialize(const QString &name, const QString &symbol, double mass, double probability) */ bool Isotope::initializeVersion1(const QString &text) { qDebug() << "Initializing Isotope with text:" << text; // This version has a set of data matching this structure: // enum class IsotopeFields // { // ID = 0, // ELEMENT = 1, // SYMBOL = 2, // ATOMIC_NUMBER = 3, // MASS = 4, // MASS_NUMBER = 5, // EXTRA_NEUTRONS = 6, // PROBABILITY = 7, // LN_PROBABILITY = 8, // RADIOACTIVE = 9, // LAST = 10, // }; // Start by resetting this Isotope instance (sets m_isValid to false) clear(); if(text.isEmpty()) { qCritical() << "The text used to initialize the Isotope instance is empty."; m_isValid = false; return m_isValid; } double temp_value_double; QString temp_value_string; QRegularExpression regexp; // At this point deconstruct the line. Make sure we remove all spaces from // beginning and end AND reduce all multiple-space substrings in the text to // only single-space substrings. QString local_text = text.simplified(); qDebug() << "After simplification, the text becomes:" << local_text; QStringList string_list = local_text.split(','); qDebug() << "After splitting text at commas, count of the subtext strings:" << string_list.size(); if(string_list.size() != 10) { qDebug() << "The text does not match an Isotope definition."; return false; } bool ok = false; // The element name temp_value_string = string_list[static_cast(Version1IsotopeFields::ELEMENT)].simplified(); // The element name can be checked using a regexp regexp.setPattern("^[a-z]+$"); if(!regexp.match(temp_value_string).hasMatch()) { qDebug() << "Failed to extract the element name."; return false; } m_name = temp_value_string; temp_value_string = string_list[static_cast(Version1IsotopeFields::SYMBOL)].simplified(); // The symbol can be checked using a regexp regexp.setPattern("^[A-Z][a-z]*$"); if(!regexp.match(temp_value_string).hasMatch()) { qDebug() << "Failed to extract the element symbol."; return false; } m_symbol = temp_value_string; temp_value_double = string_list[static_cast(Version1IsotopeFields::MASS)] .simplified() .toDouble(&ok); if(!ok) { qDebug() << "Failed to extract the isotope mass."; return false; } m_mass = temp_value_double; temp_value_double = string_list[static_cast(Version1IsotopeFields::PROBABILITY)] .simplified() .toDouble(&ok); if(!ok) { qDebug() << "Failed to extract the isotope probability."; return false; } m_probability = temp_value_double; // qDebug() << toString(); return true; } /*! \brief Initializes the \l{Isotope} using all the data in the \a text string. The string passed as argument is QString::simplified() and QString::split() with ',' as the delimiter. The index of the substring in the string list obtained by splitting the initial text at ',' delimiter defines what data element it corresponds to. Returns true if the string contained valid substrings that successfully initialized the \l{Isotope}, false otherwise. \sa Isotope(const QString &text, QObject *parent), initialize(const QString &name, const QString &symbol, double mass, double probability) */ bool Isotope::initializeVersion2(const QString &text) { qDebug() << "Initializing Isotope with text:" << text; // Start by resetting this Isotope instance (sets m_isValid to false) clear(); if(text.isEmpty()) { qCritical() << "The text used to initialize the Isotope instance is empty."; m_isValid = false; return m_isValid; } double temp_value_double; QString temp_value_string; QRegularExpression regexp; // At this point deconstruct the line. Make sure we remove all spaces from // beginning and end AND reduce all multiple-space substrings in the text to // only single-space substrings. QString local_text = text.simplified(); qDebug() << "After simplification, the text becomes:" << local_text; QStringList string_list = local_text.split(','); qDebug() << "After splitting text at commas, count of the subtext strings:" << string_list.size(); // qDebug() << "Expecting " << static_cast(IsotopeFields::LAST) // << "comma-separated fields for isotope-describing text line." // << "QStringList is:" << string_list; if(string_list.size() != static_cast(IsotopeFields::LAST)) { qWarning() << "The text does not match an Isotope definition."; m_isValid = false; return m_isValid; } bool ok = false; temp_value_string = string_list[static_cast(IsotopeFields::NAME)].simplified(); regexp.setPattern("^[a-z]+$"); if(!regexp.match(temp_value_string).hasMatch()) { qWarning() << "Failed to extract the element name."; m_isValid = false; return m_isValid; } else m_name = temp_value_string; temp_value_string = string_list[static_cast(IsotopeFields::SYMBOL)].simplified(); regexp.setPattern("^[A-Z][a-z]*$"); if(!regexp.match(temp_value_string).hasMatch()) { qWarning() << "Failed to extract the element symbol."; m_isValid = false; return m_isValid; } else m_symbol = temp_value_string; temp_value_double = string_list[static_cast(IsotopeFields::MASS)].simplified().toDouble( &ok); if(!ok) { qWarning() << "Failed to extract the isotope mass."; m_isValid = false; return m_isValid; } else m_mass = temp_value_double; temp_value_double = string_list[static_cast(IsotopeFields::PROBABILITY)] .simplified() .toDouble(&ok); if(!ok || temp_value_double > 1) { qWarning() << "Failed to extract the isotope probability."; m_isValid = false; return m_isValid; } else m_probability = temp_value_double; // At this point, it looks like the Isotope is syntactically valid, // but is it from a chemical standpoint? QVector error_list; m_isValid = validate(&error_list); if(!m_isValid) { QString errors; for(const QString &error : error_list) errors.append(QString("%1\n").arg(error)); qWarning().noquote() << "The initialized isotope is not valid, with errors:" << errors; } return m_isValid; } /*! \brief Sets the name of the isotope to \a name. \sa getName() */ void Isotope::setName(const QString &name) { QRegularExpression regexp("^[a-z]+$"); if(!regexp.match(name).hasMatch()) { qWarning() << "Failed to set the element name. The name should be all " "lower-case. Isotope left unchanged."; m_isValid = false; } m_name = name; } /*! \brief Returns the name of the isotope. \sa setName() */ QString Isotope::getName() const { return m_name; } /*! \brief Sets the symbol of the isotope to \a symbol. \sa getSymbol() */ void Isotope::setSymbol(const QString &symbol) { QRegularExpression regexp("^[A-Z][a-z]*$"); if(!regexp.match(symbol).hasMatch()) { qWarning() << "Failed to set the element symbol. Isotope left unchanged."; m_isValid = false; } m_symbol = symbol; } /*! \brief Returns the symbol of the isotope. \sa setSymbol() */ QString Isotope::getSymbol() const { return m_symbol; } /*! \brief Sets the the mass of the isotope to \a mass. \sa getMass() */ void Isotope::setMass(double mass) { if(mass < 0) { qWarning() << "Failed to set the mass. Isotope left unchanged."; m_isValid = false; } m_mass = mass; } /*! \brief Returns the mass of the isotope. \sa setMass() */ double Isotope::getMass() const { return m_mass; } /*! \brief Sets the probability (the abundance) of the isotope to \a probability. \sa getProbability() */ void Isotope::setProbability(double probability) { if(probability < 0 || probability > 1) { qWarning() << "Failed to set the probability. Isotope left unchanged."; m_isValid = false; } m_probability = probability; } /*! \brief Returns the probability (the abundance) of the isotope. \sa setProbability() */ double Isotope::getProbability() const { return m_probability; } /*! \brief Validates the isotope. The element name, symbol, mass and probability member data are scrutinized and if errors are detected descriptive error messages are added to \a error_list_p. For example, for symbol: \code if(m_symbol.isEmpty()) { local_text = m_symbol.simplified(); regexp.setPattern("^[A-Z][a-z]*$"); if(!regexp.match(local_text).hasMatch()) { error_msg = "Failed to validate the element's symbol."; error_list_p->push_back(error_msg); qWarning() << error_msg; } } \endcode Returns the error count. If no error occurred, the returned value is 0. */ bool Isotope::validate(ErrorList *error_list_p) { qsizetype previous_error_count = error_list_p->size(); QString error_msg; QString local_text; QRegularExpression regexp; //////// The element name local_text = m_name.simplified(); regexp.setPattern("^[a-z]+$"); if(!regexp.match(local_text).hasMatch()) { error_msg = "Failed to validate the element's name."; error_list_p->push_back(error_msg); qWarning() << error_msg; } m_name = local_text; //////// The element symbol local_text = m_symbol.simplified(); regexp.setPattern("^[A-Z][a-z]*$"); if(!regexp.match(local_text).hasMatch()) { error_msg = "Failed to validate the element's symbol."; error_list_p->push_back(error_msg); qWarning() << error_msg; } m_symbol = local_text; //////// The element mass if(m_mass < 0) { error_msg = "Failed to validate the isotope's mass."; error_list_p->push_back(error_msg); qWarning() << error_msg; } //////// The element probability if(m_probability < 0 || m_probability > 1) { qDebug() << "The probability:" << m_probability; error_msg = "Failed to validate the isotope's probability."; error_list_p->push_back(error_msg); qWarning() << error_msg; } if(error_list_p->size() > previous_error_count) m_isValid = false; else m_isValid = true; return m_isValid; } /*! \brief Return the member datum \l{m_isValid}. */ bool Isotope::isValid() const { return m_isValid; } /*! \brief Tests the equality between this isotope and \a other. Each member datum in \a other is compared to this isotope's member datum. If a difference is detected, a counter is incremented. Returns true if no difference has been encountered (the counter is 0) and false otherwise. \sa operator!=() */ bool Isotope::operator==(const Isotope &other) const { if(&other == this) return true; return (m_name == other.m_name && m_symbol == other.m_symbol && m_mass == other.m_mass && m_probability == other.m_probability); } /*! \brief Tests the inequality between this isotope and \a other. Returns the negated result of operator==(). \sa operator==() */ bool Isotope::operator!=(const Isotope &other) const { if(&other == this) return false; return !operator==(other); } /*! \brief Returns a string containing a comma-separated textual representation of all the member data values. All the member data values are separated using commas ','. Only the values are stored in the string, without naming the variables: \code return QString("%1,%2,%3,%4") .arg(m_name) .arg(m_symbol) .arg(m_mass, 0, 'f', 60) .arg(m_probability, 0, 'f', 60); \endcode */ QString Isotope::toString() const { // We need to use CSV because there might be spaces in the // text in the IsoSpec tables. return QString("%1,%2,%3,%4") .arg(m_name) .arg(m_symbol) .arg(m_mass, 0, 'f', 60) .arg(m_probability, 0, 'f', 60); } void Isotope::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register Isotope class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&Isotope::staticMetaObject); engine->globalObject().setProperty("Isotope", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicClusterGenerator.cpp000664 001750 001750 00000103476 15100504560 030004 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include /////////////////////// Qt includes #include /////////////////////// IsoSpec #include #include // extern const int elem_table_atomicNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int elem_table_massNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int // elem_table_extraNeutrons[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const bool elem_table_Radioactive[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_log_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; /////////////////////// pappsomspp includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicClusterGenerator \inmodule libXpertMassCore \ingroup XpertMassCoreMassCalculations \inheaderfile IsotopicClusterGenerator.hpp \brief The IsotopicClusterGenerator class provides the features needed to model isotopic clusters starting from (elemental-composition, charge) pairs. The modelling uses the member isotopic data. The generated isotopic clusters only contain cluster centroid peaks. If peaks should have a profile, then they need to be shaped. \sa IsotopicClusterShaper, MassPeakShaper */ /*! \enum MsXpS::libXpertMassCore::IsotopicDataType This enum specifies the type of isotopic data. \value NOT_SET: Not configured. . \value LIBRARY_CONFIG: The isotopic data are loaded intact from the IsoSpec library data and are considered pristine natural abundance data. . \value USER_CONFIG: The isotopic data are in the same format as for LIBRARY_CONFIG but might have been modified by the user to configure new abundances. . \value MANUAL_CONFIG: The isotopic data are in a specific format, different than the two above, that actually crafts the isotopes starting from scratch. */ /*! \typealias MsXpS::libXpertMassCore::FormulaChargePair Alias for std::pair. */ /*! \typealias MsXpS::libXpertMassCore::IsotopicClusterChargePair Alias for std::pair. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicClusterGeneratorSPtr \relates IsotopicClusterGenerator. Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicClusterGeneratorCstSPtr \relates IsotopicClusterGenerator. Synonym for std::shared_ptr. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::msp_isotopicData \brief The isotopic data needed for the computations. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_isotopicDataType \brief The \l IsotopicDataType type of data. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_maxSummedProbability \brief The summed probability of all the isotopic cluster peaks. The computation stops when this probability is reached. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_normalizeIntensity \brief The most intense cluster peak's intensity that is used to normalize all the other cluster peaks. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_sortType \brief The type of sorting required for the generated cluster peak centroids. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_sortOrder \brief The order of the sorting for the generated cluster peak centroids. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_formulaChargePairs \brief The set of (elemental composition, charge) pairs. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterGenerator::m_isotopicClusterChargePairs \brief The set of (isotopic cluster, charge) pairs. */ /*! \brief Constructs a IsotopicClusterGenerator instance. */ IsotopicClusterGenerator::IsotopicClusterGenerator() { } /*! \brief Constructs a IsotopicClusterGenerator instance. \list \li \a isotopic_data_sp: The isotopic data used for the calculations. \endlist */ IsotopicClusterGenerator::IsotopicClusterGenerator( libXpertMassCore::IsotopicDataSPtr isotopic_data_sp) : msp_isotopicData(isotopic_data_sp) { } /*! \brief Destructs this IsotopicClusterGenerator instance. */ IsotopicClusterGenerator::~IsotopicClusterGenerator() { // qDebug(); } /*! \brief Sets the isotopic data type to \a isotopic_data_type. */ void IsotopicClusterGenerator::setIsotopicDataType( IsotopicDataType isotopic_data_type) { m_isotopicDataType = isotopic_data_type; } /*! \brief Sets the isotopic data to \a isotopic_data_sp. */ void IsotopicClusterGenerator::setIsotopicData( libXpertMassCore::IsotopicDataSPtr isotopic_data_sp) { msp_isotopicData = isotopic_data_sp; } /*! \brief Returns the isotopic data. */ libXpertMassCore::IsotopicDataSPtr IsotopicClusterGenerator::getIsotopicData() const { return msp_isotopicData; } /*! \brief Adds the (elemental composition, charge) pair \a formula_charge_pair to the member list of FormulaChargePair instances. The member list of FormulaChargePair instances is first cleared. */ void IsotopicClusterGenerator::setFormulaChargePair( FormulaChargePair &formula_charge_pair) { m_formulaChargePairs.clear(); m_formulaChargePairs.push_back(formula_charge_pair); // qDebug() << formula_charge_pair.first << formula_charge_pair.second; } /*! \brief Adds the (elemental composition, charge) pair \a formula_charge_pair to the member list of FormulaChargePair instances. */ void IsotopicClusterGenerator::appendFormulaChargePair( FormulaChargePair &formula_charge_pair) { m_formulaChargePairs.push_back(formula_charge_pair); // qDebug() << formula_charge_pair.first << formula_charge_pair.second; } /*! \brief Adds the (elemental composition, charge) pairs \a formula_charge_pairs to the member list of FormulaChargePair instances. The member list of FormulaChargePair instances is first cleared. */ void IsotopicClusterGenerator::setFormulaChargePairs( const std::vector &formula_charge_pairs) { m_formulaChargePairs.clear(); m_formulaChargePairs.assign(formula_charge_pairs.begin(), formula_charge_pairs.end()); // qDebug() << "Set" << m_formulaChargePairs.size() << "formula/charge pairs"; // for(auto pair : m_formulaChargePairs) // qDebug() << pair.first << "/" << pair.second; } /*! \brief Sets the summed probability maximum value to \a max_probability. */ void IsotopicClusterGenerator::setMaxSummedProbability(double max_probability) { m_maxSummedProbability = max_probability; } /*! \brief Sets the normalization intensity to \a normalize_intensity. */ void IsotopicClusterGenerator::setNormalizationIntensity(int normalize_intensity) { m_normalizeIntensity = normalize_intensity; } /*! \brief Sets the \a sort_type. */ void IsotopicClusterGenerator::setSortType(pappso::Enums::SortType sort_type) { m_sortType = sort_type; } /*! \brief Sets the \a sort_order. */ void IsotopicClusterGenerator::setSortOrder(pappso::Enums::SortOrder sort_order) { m_sortOrder = sort_order; } /*! \brief Validates the elemental composition \a formula. The \a formula needs to be fully indexed, that is, even an atom present only once needs to be indexed with '1', like this \c H2O1. Returns true if validation was successful, false otherwise. \sa Formula::validate() */ bool IsotopicClusterGenerator::validateFormula(Formula &formula) { // qDebug() << "Checking formula:" << formula.toString() //<< "against an isotopic data set of " << msp_isotopicData->size() //<< "isotopes"; // Check the syntax of the formula. Note that we need an obligatory count // index even for elements that are present a single time (H2O1 note the // 1). // IsoSpec requires that even single-count element be qualified with an // index (H2O1) formula.setForceCountIndex(true); // We have to validate because the formula might be "C5H6N3-O1", in which case // if would fail the simple checkSyntax() call (that call needs to be used on // already split parts of a formula). ErrorList error_list; if(!formula.validate( msp_isotopicData, true /*store atom count*/, true /*reset counts*/, &error_list)) return false; return true; } /*! \brief Validates all the elemental compositions in this IsotopicClusterGenerator instance. Each \l FormulaChargePair in m_formulaChargePairs is validated for its elemental composition by first creating a \l Formula out of it. Returns true if validation was successful, false otherwise. \sa validateFormula, Formula::validate() */ bool IsotopicClusterGenerator::validateAllFormulas() { for(FormulaChargePair &pair : m_formulaChargePairs) { Formula formula(pair.first); if(!validateFormula(formula)) return false; } return true; } /*! \brief Runs the IsoSpec-based isotopic calculations. \list \li \a element_count: the number of elements in the chemical composition. \li \a charge: the charge of the analyte. \li \a per_element_isotopes_count_array_p: pointer to int array. This array lists the number of isotopes that each element has. Typically, C has 2, O has 3, P has 1... \li \a per_element_symbol_count_array_p: pointer to int array. This array lists the count of atoms of each element. Typically H20 will have 2 for H and 1 for O. \li \a per_element_isotope_masses_arrays_p_p: pointer to pointer to double array (array of arrays of double). Each array contains a new sub-array for each symbol. The sub-array contains the isotopic masses for one of the element symbols. \li \a per_element_isotope_probs_arrays_p_p: pointer to pointer to double array (array of arrays of double). Each array contains a new sub-array for each symbol. The sub-array contains the isotopic probs for one of the element symbols. \endlist Returns a pappso::Trace with the calculated isotopic cluster. */ pappso::TraceSPtr IsotopicClusterGenerator::runIsotopicDataCalculations( std::size_t element_count, int charge, int *per_element_isotopes_count_array_p, int *per_element_symbol_count_array_p, double **per_element_isotope_masses_arrays_p_p, double **per_element_isotope_probs_arrays_p_p) { // We get all the isotopic data relevant to the isotopic cluster modelling // calculation as performed by the IsoSpec library. if(per_element_isotopes_count_array_p == nullptr || per_element_symbol_count_array_p == nullptr || per_element_isotope_masses_arrays_p_p == nullptr || per_element_isotope_probs_arrays_p_p == nullptr) qFatal("Programming error. The pointers cannot be nullptr."); if(m_maxSummedProbability <= 0 || m_maxSummedProbability > 1) { qDebug() << "The maximum summed probability has an incorrect value:" << m_maxSummedProbability; return nullptr; } IsoSpec::IsoLayeredGenerator iso( IsoSpec::Iso(element_count, per_element_isotopes_count_array_p, per_element_symbol_count_array_p, per_element_isotope_masses_arrays_p_p, per_element_isotope_probs_arrays_p_p), // The three values below are from the documentation (default values in the // constructor). We have added them 20230329 because we discovered that they // were needed on minGW64, otherwise we would experience crashes. 1000, 1000, true, m_maxSummedProbability); // qDebug() << "iso's mono peak mass:" << iso.getMonoisotopicPeakMass(); // Each time we run a calculation, we do store the results in a new // IsotopicCluster. pappso::TraceSPtr isotopic_cluster_sp = std::make_shared(); // We store the results as std::vector> // because we'll want to sort the values according to the user's requirements. double effective_summed_probs = 0; // Iterate in all the cluster configurations and output all the ones that // summatively make a total probability <= to the probability set by the // user. /////////////// ATTENTION //////////////// // The loop below is tricky, while(iso.advanceToNextConfiguration()) { double iso_prob = iso.prob(); double iso_mz = iso.mass() / charge; // qDebug() << "For current configuration (charge accounted for):" << // iso_mz //<< iso_prob //<< "and effective_probs_sum : " << effective_summed_probs; // Create a peak centroid and store it (remark that we change the mass // of the ion into m/z because the user had set the charge corresponding // to the formula for which the isotopic cluster is being computed. isotopic_cluster_sp->push_back(pappso::DataPoint(iso_mz, iso_prob)); // qDebug() << "Pushed back new peak centroid:" << iso_mz << "/" << // iso_prob; // We do this increment at the end of the block. Indeed, if we had set up // at the top of the block, then, if the very first centroid had already a // prob > m_maxSummedProbability, then we would end up with an empty // cluster! effective_summed_probs += iso_prob; if(effective_summed_probs > m_maxSummedProbability) { // qDebug() << "Reached the max value: effective_summed_probs:" //<< effective_summed_probs //<< "and m_maxSummedProbability:" << m_maxSummedProbability //<< "BREAKING."; break; } else { // qDebug() << "Not yet reached the max value: effective_summed_probs // : " //<< effective_summed_probs //<< "and m_maxSummedProbability:" << m_maxSummedProbability; } } // Now perform the normalization to the Gaussian apex intensity value if // so is requested by the user For this we first need to find // what is the most intensity peak centroid. Then, we'll normalize against // it by dividing all intensity that that most intense value and // multiplying by the requested gaussian apex intensity value. // qDebug() << "Now asking for normalization."; // Just a debug check. // qDebug() << "Before normalizing, first data point:" //<< isotopic_cluster_sp->front().toString(); normalizeIntensities(isotopic_cluster_sp); // Just a debug check. // qDebug() << "After normalizing, first data point:" //<< isotopic_cluster_sp->front().toString(); // Now check if the user requests to kind of sorting of the PeakCentroid // instances. sortPeakCentroids(isotopic_cluster_sp); // qDebug() << "Now returning a cluster of size:" << // isotopic_cluster_sp->size(); return isotopic_cluster_sp; } /*! \brief Calculates the isotopic cluster's peak centroids for \a formula_charge_pair. \list 1 \li The elemental composition formula string is converted to a \l Formula and validated. \li The proper isotopic data handler is allocated (\l IsotopicDataType). \li The number of symbols in the elemental composition is determined. \li The int arrays and arrays of double arrays are allocated. \li The arrays are filled-in with \l configureIsotopicData(). \li The calculations are performed on these arrays with \l runIsotopicDataCalculations(). \endlist Returns the results of the computation in the form of a IsotopicClusterChargePair instance. */ IsotopicClusterChargePair IsotopicClusterGenerator::generateIsotopicClusterCentroids( FormulaChargePair formula_charge_pair) { // qDebug() << "Starting generation of isotopic cluster for formula:" //<< formula_charge_pair.first; Formula formula(formula_charge_pair.first); // The check will generate useful data inside the Formula! if(!validateFormula(formula)) return std::pair(std::make_shared(), 0); // Use the correct handler! std::unique_ptr isotopic_data_handler_up = nullptr; if(m_isotopicDataType == IsotopicDataType::LIBRARY_CONFIG) { isotopic_data_handler_up = std::make_unique(msp_isotopicData); } else if(m_isotopicDataType == IsotopicDataType::USER_CONFIG) { isotopic_data_handler_up = std::make_unique(msp_isotopicData); } else if(m_isotopicDataType == IsotopicDataType::MANUAL_CONFIG) { isotopic_data_handler_up = std::make_unique(msp_isotopicData); } else qFatal("Programming error. The isotopic data type is not correct."); // At this point we need to create the arrays exactly as we do in the user // manual config. So we need to know how many different chemical element // symbols we have in the formula. std::map symbol_double_count_map = formula.getSymbolCountMapCstRef(); std::size_t element_count = symbol_double_count_map.size(); // qDebug() << "Number of different symbols in the formula:" << element_count; if(!element_count) { qDebug() << "There is not a single element in the Formula."; return std::pair(std::make_shared(), 0); } // qDebug() << "The validated formula has a symbol/count map size:" //<< element_count; // We have to copy the symbol/count map obtained by validating // the formula into the isotopic data handler. That map is essential for the // crafting by the handler of the different IsoSpec arrays. // At this point, all the elements defined by the user have been completed and // we'll have to create the static arrays that are needed by IsoSpec. // We now need to construct the C arrays for IsoSpec. The arrays need to // be filled-in very accurately. // This array lists the number of isotopes that each element has. // Typically, C has 2, O has 3, P has 1... int *per_element_isotopes_count_array_p = nullptr; // This array lists the count of atoms of each element. // Typically H20 will have 2 for H and 1 for O. int *per_element_symbol_count_array_p = nullptr; // These are arrays of arrays! Each array contains a new sub-array for each // symbol. The sub-array contains the isotopic masses (or probs) for one of // the element symbols. The sub-arrays are allocated by the handler below. double **per_element_isotope_masses_arrays_p_p = nullptr; double **per_element_isotope_probs_arrays_p_p = nullptr; // The isotopic cluster calculations do not understand // formulas with double counts for the symbols!! // We thus need to convert the symbol_count_map that is // -based to a map that has its second // member, not of double type but of int type. std::map symbol_int_count_map; std::map::const_iterator iter = symbol_double_count_map.begin(); while(iter != symbol_double_count_map.end()) { symbol_int_count_map[iter->first] = static_cast(iter->second); // qDebug() << "Old " << iter->first << "double version:" << iter->second // << "new int version:" << symbol_int_count_map[iter->first]; ++iter; } // We pass the array pointers by reference. if(!configureIsotopicData(symbol_int_count_map, per_element_isotopes_count_array_p, per_element_symbol_count_array_p, per_element_isotope_masses_arrays_p_p, per_element_isotope_probs_arrays_p_p)) { qDebug() << "Failed to actually prepare the isotopic data tables for the " "computation."; return std::pair(std::make_shared(), 0); } // At this point we have all the arrays needed to work. pappso::TraceSPtr isotopic_cluster_sp = runIsotopicDataCalculations(element_count, formula_charge_pair.second, per_element_isotopes_count_array_p, per_element_symbol_count_array_p, per_element_isotope_masses_arrays_p_p, per_element_isotope_probs_arrays_p_p); // FIXME // To avoid a memory leak, we need to delete the mass and prob heap-allocated // arrays. // delete[] per_element_isotopes_count_array_p; // delete[] per_element_symbol_count_array_p; // for(std::size_t iter = 0; iter < element_count; ++iter) //{ // delete[] per_element_isotope_masses_arrays_p_p[iter]; // delete[] per_element_isotope_probs_arrays_p_p[iter]; //} if(isotopic_cluster_sp == nullptr) qDebug() << "Failed to compute an isotopic cluster for formula:" << formula_charge_pair.first; // Just a debug check. // qDebug() << "After normalizing, first data point:" //<< isotopic_cluster_sp->front().toString(); // qDebug() << "Done generating cluster for formula:" //<< formula_charge_pair.first; return std::pair(isotopic_cluster_sp, formula_charge_pair.second); } /*! \brief Configures the isotopic data in a set of arrays for the (symbol,count) pairs in \a symbol_count_map. \list \li \a per_element_isotopes_count_array_p: pointer to int array. This array lists the number of isotopes that each element has. Typically, C has 2, O has 3, P has 1... \li \a per_element_symbol_count_array_p: pointer to int array. This array lists the count of atoms of each element. Typically H20 will have 2 for H and 1 for O. \li \a per_element_isotope_masses_arrays_p_p: pointer to pointer to double array (array of arrays of double). Each array contains a new sub-array for each symbol. The sub-array contains the isotopic masses for one of the element symbols. \li \a per_element_isotope_probs_arrays_p_p: pointer to pointer to double array (array of arrays of double). Each array contains a new sub-array for each symbol. The sub-array contains the isotopic probs for one of the element symbols. \endlist Returns a pappso::Trace with the calculated isotopic cluster. */ bool IsotopicClusterGenerator::configureIsotopicData( std::map &symbol_count_map, int *&per_element_isotopes_count_array_p, int *&per_element_symbol_count_array_p, double **&per_element_isotope_masses_arrays_p_p, double **&per_element_isotope_probs_arrays_p_p) { // Start by allocating the arrays we'll have to feed in this configuration // work. // However, we need to ensure that we actually have some stuff to work on. // That stuff is kind of a formula in the form of the std::map // m_symbolCountMap that pairs the symbols of the atoms in the formula and // the count for each symbol. if(!symbol_count_map.size()) return false; // qDebug() << "The isotopic data have" << msp_isotopicData->size() //<< "isotopes"; // Example used in the comments below: glucose, C6H12O6. // How many isotopes of each element symbols are there? // C:2, H:2, O:3 per_element_isotopes_count_array_p = new int[symbol_count_map.size()]; // How many atoms of each chemical element symbol are there? // C:6, H:12, O:6 per_element_symbol_count_array_p = new int[symbol_count_map.size()]; // Each subarray contains the mass of one of the isotopes for the element // symbol. // First array for C (two masses), second array for H (two masses), third // array for O (three masses). per_element_isotope_masses_arrays_p_p = new double *[symbol_count_map.size()]; // Each subarray contains the prob of one of the isotopes for the element // symbol. // First array for C (two probs), second array for H (two probs), third // array for O (three probs). per_element_isotope_probs_arrays_p_p = new double *[symbol_count_map.size()]; // Index that will allow to address the right slot in the arrays being // filled with isotopic data. int current_symbol_index = 0; for(auto item : symbol_count_map) { QString symbol = item.first; int symbol_count = item.second; // qDebug() << "Iterating in symbol/count:" << symbol << "/" << // symbol_count; // Immediately fill-in the symbol count value. per_element_symbol_count_array_p[current_symbol_index] = symbol_count; // Now get iterator bounding the isotopes for this symbol. std::pair::const_iterator, QList::const_iterator> iter_pair = msp_isotopicData->getIsotopesBySymbol(symbol); // Handy shortcuts QList::const_iterator iter = iter_pair.first; QList::const_iterator iter_end = iter_pair.second; qsizetype isotope_count = std::distance(iter, iter_end); // qDebug() << "For symbol:" << symbol << "there are:" << isotope_count //<< "isotopes"; // Sanity check if(isotope_count != msp_isotopicData->getIsotopeCountBySymbol(symbol)) qFatal( "Programming error. The is something wrong with the isotopic " "data."); // Fill-in the isotope count for the current symbol. per_element_isotopes_count_array_p[current_symbol_index] = isotope_count; // Fill-in the isotopes (mass/prob). For each element symbol in the // symbol/count map, we allocate a double array the size of the number // of isotopes so as to store the mass of each isotope (same for the // probs later). Once we have done that fill-in, we can set the address // of the array of the array of array below. // Allocate a double array for the masses and another one for the probs. double *masses_p = new double[isotope_count]; double *probs_p = new double[isotope_count]; int current_isotope_index = 0; while(iter != iter_end) { IsotopeQSPtr isotope_qsp = *iter; // qDebug() << "For symbol" << symbol << "iterating with index" //<< current_isotope_index << "with Isotope *" //<< isotope_qsp.get(); masses_p[current_isotope_index] = isotope_qsp->getMass(); probs_p[current_isotope_index] = isotope_qsp->getProbability(); // Increment the iterator ++iter; // Increment the isotope index so that we fill next array cell. ++current_isotope_index; } // At this time the masses and probs for the isotopes of current symbol // have been filled-in. per_element_isotope_masses_arrays_p_p[current_symbol_index] = masses_p; per_element_isotope_probs_arrays_p_p[current_symbol_index] = probs_p; // We need to increment this index so as to address the right slot in // the arrays! ++current_symbol_index; } // At this point we have documented all the isotopic data required to // perform a calculation with IsoSpec. return true; } /*! \brief Normalizes the intensities of the isotopic cluster's peak centroids in \a isotopic_cluster_sp. If normalization is asked for, the most intense peak centroid in \a isotopic_cluster_sp is determined. That intensity becomes the m_normalizeIntensity value and all the other peak centroids' intensities are normalized. \note The normalization occurs \e{in place}. */ void IsotopicClusterGenerator::normalizeIntensities( pappso::TraceSPtr &isotopic_cluster_sp) { // qDebug() << "The normalize_intensity is:" << m_normalizeIntensity; // The most intense peak centroid's intensity value needs to be set to the // requested value and the same change ratio needs to be applied to all the // other peak centroids. // No normalization is asked for. if(m_normalizeIntensity == std::numeric_limits::min()) { // qDebug() << "No normalization was asked for. Skipping."; return; } if(!isotopic_cluster_sp->size()) { qDebug() << "The isotopic cluster has not a single data point."; return; } // First get the most intense centroid. double max_found_intensity = 0; // qDebug() << "isotopic_cluster_sp->size():" << isotopic_cluster_sp->size(); pappso::Trace::iterator vector_iterator = std::max_element( isotopic_cluster_sp->begin(), isotopic_cluster_sp->end(), [](const pappso::DataPoint first, const pappso::DataPoint second) { return first.y < second.y; }); if(vector_iterator == isotopic_cluster_sp->end()) qFatal("Programming error"); max_found_intensity = (*vector_iterator).y; if(!max_found_intensity) qFatal("The maximum intensity of the whole isotopic cluster is 0."); // qDebug().noquote() << "Peak centroid with maximum intensity: " //<< vector_iterator->toString(); // Calculate the ratio between the final requested intensity and the greatest // intensity. That ratio will be multiplied to intensity of the each peak // centroid, which is why we call it a factor. double intensity_factor = m_normalizeIntensity / max_found_intensity; // qDebug() << "Will multiply this intensity factor to each data point's " //"intensity value:" //<< intensity_factor; // Just a debug check. // qDebug() << "Before normalizing, first data point:" //<< isotopic_cluster_sp->front().toString(); std::for_each( isotopic_cluster_sp->begin(), isotopic_cluster_sp->end(), [intensity_factor](pappso::DataPoint &data_point) // modify in-place { // qDebug() << "Before normalization:" << // data_point.toString(); double normalized_intensity = data_point.y * intensity_factor; data_point.y = normalized_intensity; // qDebug() << "After normalization:" << // data_point.toString(); }); // At this point the centroids' intensity have been normalized. // Just a debug check. // qDebug() << "After normalizing, first data point:" //<< isotopic_cluster_sp->front().toString(); } /*! \brief Sorts the peak centroids of the isotopic cluster \a isotopic_cluster_sp. The sort is performed according to \l m_sortType. */ void IsotopicClusterGenerator::sortPeakCentroids( pappso::TraceSPtr &isotopic_cluster_sp) { if(m_sortType == pappso::Enums::SortType::none) return; return isotopic_cluster_sp->sort(m_sortType, m_sortOrder); } /*! \brief Returns a string containing a space-separated set of m/z, intensity pairs, representing the isotopic cluster in \a isotopic_cluster_sp. */ QString IsotopicClusterGenerator::clusterToString( const pappso::TraceCstSPtr &isotopic_cluster_sp) const { // Export the results as a string. Note how we do export the relative // intensity. If there was normalization, that value was updated, otherwise it // had been initialized identical to the intensity upon creation of the // PeakCentroid instances in the vector. QString text; for(const pappso::DataPoint &dp : *isotopic_cluster_sp) { text += QString("%1 %2\n").arg(dp.x, 0, 'f', 30).arg(dp.y, 0, 'f', 30); } // qDebug().noquote() << "Cluster to string: " << text; return text; } /*! \brief Returns a string containing a space-separated set of m/z, intensity pairs, representing the isotopic clusters in the member isotopic clusters \l m_isotopicClusterChargePairs. */ QString IsotopicClusterGenerator::clustersToString() const { // Export the results as a string. Note how we do export the relative // intensity. If there was normalization, that value was updated, otherwise it // had been initialized identical to the intensity upon creation of the // PeakCentroid instances in the vector. QString text; for(auto isotopic_cluster_charge_pair : m_isotopicClusterChargePairs) { text += clusterToString(isotopic_cluster_charge_pair.first); } // qDebug().noquote() << text; return text; } /*! \brief Starts the computations. The member m_isotopicClusterChargePairs are first cleared. Returns the count of IsotopicClusterChargePair instances generated upon the calculations. */ std::size_t IsotopicClusterGenerator::run() { // Iterate in all the formula/charge pairs and for each compute an isotopic // cluster. m_isotopicClusterChargePairs.clear(); for(auto formula_charge_pair : m_formulaChargePairs) { IsotopicClusterChargePair pair = generateIsotopicClusterCentroids(formula_charge_pair); if(!pair.first->size()) { qFatal("The isotopic cluster is empty for formula: %s", formula_charge_pair.first.toLatin1().data()); } m_isotopicClusterChargePairs.push_back(pair); } return m_isotopicClusterChargePairs.size(); } /*! \brief Returns the member list ofIsotopicClusterChargePair instances. */ const std::vector & IsotopicClusterGenerator::getIsotopicClusterChargePairs() const { return m_isotopicClusterChargePairs; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicClusterShaper.cpp000664 001750 001750 00000051503 15100504560 027271 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include // for std::numeric_limits /////////////////////// Qt includes /////////////////////// pappsomspp includes #include #include #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterShaper.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicClusterShaper \inmodule libXpertMassCore \ingroup XpertMassCoreMassCalculations \inheaderfile IsotopicClusterShaper.hpp \brief The IsotopicClusterShaper class provides the features needed to shape sets of (peak centroid m/z, intensity) pairs associated to a given charge into a mass spectral pappso;:Trace. Each set of (peak centroid m/z, intensity) pairs corresponds to an isotopic cluster that is associated to a charge. The configuration of the peak shaping process is held in a specific \l MassPeakShaperConfig class. The output of the computation is a pappso::Trace obtained by combining all the different shapes obtained for the different peak centroids of all the sets of (peak centroid m/z, intensity) pairs. If binning was requested, the obtained Trace is the result of a combination accounting for the required bin size, otherwise the obtained Trace is the result of the mere addition of all the points in the different traces. \sa MassPeakShaperConfig */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_isotopicClusterChargePairs \brief Vector of pappso::Trace instances in pair with charges. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::mp_massPeakShaperConfig \brief The configuration driving the mass peak shaping process. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_mapTrace \brief The map relating a m/z value to its intensity. This map is a variant of pappso::Trace that is designed to allow for easy mass spectrum combination. It is generally used only for computations and is converted to a pappso::Trace once all the computations have been carried out. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_finalTrace \brief The pappso::Trace holding the final results of the computations. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_mzIntegrationParams \brief The configuration of the mass spectral combinations (for example, determines the bins, if binning is required). */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_mostIntensePeakMz \brief The most intense peak encountered during the calculations. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_smallestMz \brief The smallest m/z value encountered during the calculations. This value is required for the crafting of the bins. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_greatestMz \brief The greatest m/z value encountered during the calculations. This value is required for the crafting of the bins. */ /*! \variable MsXpS::libXpertMassCore::IsotopicClusterShaper::m_normalizeIntensity \brief The value by which all the peak shapes need to be normalized. */ /*! \brief Constructs a IsotopicClusterShaper instance. \list \li \a isotopic_cluster: The peak centroids belonging to an isotopic cluster in the form of a pappso::Trace. \li \a charge: The charge of the analyte below the isotopic cluster peaks. \li \a config: The mass peak shaping process configuration. \endlist Each pappso::DataPoint in the \a isotopic_cluster pappso::Trace corresponds to a (m/z, intensity) peak centroid belonging to the isotopic cluster. */ IsotopicClusterShaper::IsotopicClusterShaper( const pappso::Trace &isotopic_cluster, int charge, const MassPeakShaperConfig &config) : mp_massPeakShaperConfig(new MassPeakShaperConfig(config)) { // No need to reset, we are constructing. setIsotopicCluster(isotopic_cluster, charge, false); } /*! \brief Constructs a IsotopicClusterShaper instance. \list \li \a isotopic_cluster_charge_pairs: The pairs associating a pappso::Trace instance to its corresponding charge. \li \a config: The mass peak shaping process configuration. \endlist In this constructor, a set of sets of (m/z, charge) peak centroids is passed as argument. The pappso::Trace instances in \a isotopic_cluster_charge_pairs might correspond to all the isotopic clusters representing a given analyte at different charges. */ IsotopicClusterShaper::IsotopicClusterShaper( const std::vector &isotopic_cluster_charge_pairs, const MassPeakShaperConfig &config) : mp_massPeakShaperConfig(new MassPeakShaperConfig(config)) { // No need to reset, we are constructing. setIsotopicClusterChargePairs(isotopic_cluster_charge_pairs, false); } /*! \brief Destructs this IsotopicClusterShaper instance. */ IsotopicClusterShaper::~IsotopicClusterShaper() { } /*! \brief Sets the peak shaping process \a{config}uration. */ void IsotopicClusterShaper::setConfig(const MassPeakShaperConfig &config) { mp_massPeakShaperConfig->initialize(config); } /*! \brief Gets the peak shaping process configuration. */ const MassPeakShaperConfig * IsotopicClusterShaper::getConfig() const { return mp_massPeakShaperConfig; } /*! \brief Sets the intensity normalization value to \a max_intensity. */ void IsotopicClusterShaper::setNormalizeIntensity(int max_intensity) { m_normalizeIntensity = max_intensity; } /*! \brief Gets the intensity normalization value. */ int IsotopicClusterShaper::getNormalizeIntensity() const { return m_normalizeIntensity; } /*! \brief Runs the mass peak shaping process for all the \l IsotopicClusterChargePair objects in \l m_isotopicClusterChargePairs. If \a reset is true, the member \l m_mapTrace and \l m_finalTrace are cleared before starting the computations. The \l m_config member is first \l{MassPeakShaperConfig::resolve()}d to check that all the parameters have been properly set and are valid. Returns the obtained pappso::Trace corresponding to the combination of all the individual traces obtained for the various isotopic clusters and their corresponding charge. */ pappso::Trace & IsotopicClusterShaper::run(bool reset) { if(reset) { // Clear the map trace that will receive the results of the combinations. m_mapTrace.clear(); m_finalTrace.clear(); } ErrorList error_list; if(!mp_massPeakShaperConfig->resolve(error_list)) { qDebug() << "Failed to resolve the MassPeakShaperConfig with errors:\n" << Utils::joinErrorList(error_list, "\n"); return m_finalTrace; } // This class works on a vector of pairs containing the following: // 1. a pappso::TraceCstSPtr // 2. a charge // We will process each pair in turn. If the integration requires bin, then // each shaped isotopic cluster will be combined into a mass spectrum. // Otherwise a trace combiner will be used. // When setting the data (either by construction or using the set<> functions, // we had monitored the smallest and the greatest m/z value over the whole set // of the DataPoint objects in the isotopic clusters (centroid data). This is // because we need, in case binning is required, these values to craft the // bins. // We will need to perform combinations, positive combinations. // This mass spectrum combiner is in case we need binning. pappso::MassSpectrumPlusCombiner mass_spectrum_plus_combiner; // This trace combiner is in case do *not* need binning. pappso::TracePlusCombiner trace_plus_combiner(-1); // Configure the mass spectrum combiner in case we need binning. if(mp_massPeakShaperConfig->isWithBins()) { // Bins were requested. // qDebug() << "Bins are required."; // Get the bin size out of the configuration. double bin_size = mp_massPeakShaperConfig->getBinSize(); // qDebug() << "The bin size in the config is:" << bin_size; // Because we had monitored the m/z values of all the shapes generated // above, we know the smallest and greatest m/z value that were // encountered in all that peak shaping activity. We thus can create the // bins faithfully. Enums::MassPeakWidthLogic logic = mp_massPeakShaperConfig->getMassPeakWidthLogic(); // The m_smallestMz and m_greatestMz values have been determined by // looking into the unshaped isotopic clusters passed to this object // either by construction or with functions. These two mz values are thus // peak centroids, not data points belonging to a shaped peak since we // have not yet started shaping the peaks. This means that we cannot // create bins start / ending at these values because we would loose the // first half of the first shaped peak centroid and the second half of the // last shaped peak centroid (a shaped peak goes left *and* right of the // peak centroid otherwise there would be no shape). // // This is why we provide a confortable margin fo the bin creation below // by removing 1 Th on the left of the smallest mz and by adding 1 Th on // right of the greatest mz. if(logic == Enums::MassPeakWidthLogic::FWHM) { m_mzIntegrationParams.initialize( m_smallestMz - 1, m_greatestMz + 1, pappso::MzIntegrationParams::BinningType::ARBITRARY, pappso::PrecisionFactory::getDaltonInstance(bin_size), /*binSizeDivisor*/ 1, /*decimalPlacesr*/ -1, true, nullptr); } else if(logic == Enums::MassPeakWidthLogic::RESOLUTION) { double resolution = mp_massPeakShaperConfig->getResolution(); m_mzIntegrationParams.initialize( m_smallestMz - 1, m_greatestMz + 1, pappso::MzIntegrationParams::BinningType::ARBITRARY, pappso::PrecisionFactory::getResInstance(resolution), mp_massPeakShaperConfig->getBinSizeDivisor(), -1, true, nullptr); } else qFatal( "Programming error. By this time, the mass peak width logic should " "have been defined."); // qDebug() << "The mz integration params:" //<< m_mzIntegrationParams.toString(); // Now compute the bins. std::vector bins = m_mzIntegrationParams.createBins(); // qDebug() << "The bins:" << bins; mass_spectrum_plus_combiner.setBins(bins); // qDebug() << "Set bins to the mass spectrum combiner:" //<< mass_spectrum_plus_combiner.getBins().size(); } std::size_t peak_centroid_count = 0; std::size_t isotopic_cluster_count = 0; // Iterate in the isotopic cluster/charge pairs. for(auto isotopic_cluster_charge_pair : m_isotopicClusterChargePairs) { int charge = isotopic_cluster_charge_pair.second; // Iterate in the data points of the current centroid data isotopic // cluster. for(auto data_point : *isotopic_cluster_charge_pair.first) { // Note the division by m_charge below! pappso::Trace trace = MassPeakShaper::computePeakShape( data_point.x / charge, data_point.y, *mp_massPeakShaperConfig); // qDebug() << "The shaped isotopic cluster has size:" << // trace.size(); if(trace.size()) { if(mp_massPeakShaperConfig->isWithBins()) mass_spectrum_plus_combiner.combine(m_mapTrace, trace); else trace_plus_combiner.combine(m_mapTrace, trace); // qDebug() << qSetRealNumberPrecision(15) //<< "The map trace for combination has size:" //<< m_mapTrace.size() //<< "and starting m/z:" << m_mapTrace.begin()->first //<< "and ending m/z:" //<< std::prev(m_mapTrace.end())->first; ++peak_centroid_count; } } ++isotopic_cluster_count; } // qDebug() << QString( //"Successfully processed %1 isotopic clusters for a total of %2 " //"shaped peak centroids") //.arg(isotopic_cluster_count) //.arg(peak_centroid_count); // The user might have asked that the most intense m/z peak centroid be used // for normalization. In that case that peak centroid's intensity is brought // to m_normalizeIntensity and the ratio between its current intensity and // m_normalizeIntensity is used to normalize all the other data points in the // trace. if(m_normalizeIntensity != 1) { // qDebug() << "Now normalizing to intensity = " << m_normalizeIntensity; pappso::Trace trace = m_mapTrace.toTrace(); m_finalTrace = trace.filter(pappso::FilterNormalizeIntensities(m_normalizeIntensity)); // double max_int = normalized_trace.maxYDataPoint().y; // qDebug() << "After normalization max int:" << max_int; } else m_finalTrace = m_mapTrace.toTrace(); // qDebug() << "Returning a trace of size:" << m_finalTrace.size(); // pappso::Utils::writeToFile(m_finalTrace.toString(), "/tmp/mass/trace.txt"); return m_finalTrace; } /*! \brief Handles the \a isotopic_cluster_sp input isotopic cluster as a pappso::Trace. \a isotopic_cluster_sp is associated to a \a charge. If \a reset is true, the member vector of \l IsotopicClusterChargePair instances is cleared before the computations. This function is the workhorse for all the functions used to set the initial data for the computations. Its main task is to scrutinize the data in \a isotopic_cluster_sp and update the \l m_smallestMz, \l m_greatestMz and \l m_mostIntensePeakMz values based on the data passed as argument. */ void IsotopicClusterShaper::setIsotopicCluster( pappso::TraceCstSPtr isotopic_cluster_sp, int charge, bool reset) { if(reset) m_isotopicClusterChargePairs.clear(); double min_x = isotopic_cluster_sp->minX(); m_smallestMz = std::min(m_smallestMz, min_x); double max_x = isotopic_cluster_sp->maxX(); m_greatestMz = std::max(m_greatestMz, max_x); m_mostIntensePeakMz = isotopic_cluster_sp->maxYDataPoint().x; // qDebug() << qSetRealNumberPrecision(15) << "m_smallestMz:" << m_smallestMz //<< "m_greatestMz:" << m_greatestMz //<< "m_mostIntensePeakMz:" << m_mostIntensePeakMz; m_isotopicClusterChargePairs.push_back( IsotopicClusterChargePair(isotopic_cluster_sp, charge)); mp_massPeakShaperConfig->setReferencePeakMz(m_mostIntensePeakMz); } /*! \brief Handles the \a isotopic_cluster_sp input isotopic cluster as a pappso::Trace. \a isotopic_cluster_sp is associated to a \a charge. The member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicCluster( pappso::TraceCstSPtr isotopic_cluster_sp, int charge) { setIsotopicCluster(isotopic_cluster_sp, charge, true); } /*! \brief Handles the \a isotopic_cluster input isotopic cluster as a pappso::Trace. \a isotopic_cluster is associated to a \a charge. If \a reset is true, the member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicCluster(const pappso::Trace &isotopic_cluster, int charge, bool reset) { setIsotopicCluster( std::make_shared(isotopic_cluster), charge, reset); } /*! \brief Handles the \a isotopic_cluster input isotopic cluster as a pappso::Trace. \a isotopic_cluster is associated to a \a charge. The member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicCluster(const pappso::Trace &isotopic_cluster, int charge) { setIsotopicCluster( std::make_shared(isotopic_cluster), charge, true); } /*! \brief Handles the \a isotopic_cluster_charge_pair input isotopic cluster as a IsotopicClusterChargePair. The member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicCluster( IsotopicClusterChargePair isotopic_cluster_charge_pair) { setIsotopicCluster(isotopic_cluster_charge_pair.first, isotopic_cluster_charge_pair.second, true); } /*! \brief Handles the \a isotopic_cluster_charge_pair input isotopic cluster as a IsotopicClusterChargePair. If \a reset is true, the member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicCluster( IsotopicClusterChargePair isotopic_cluster_charge_pair, bool reset) { setIsotopicCluster(isotopic_cluster_charge_pair.first, isotopic_cluster_charge_pair.second, reset); } /*! \brief Handles the \a isotopic_cluster_charge_pairs input isotopic cluster as a vector of IsotopicClusterChargePair instances. If \a reset is true, the member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicClusterChargePairs( const std::vector &isotopic_cluster_charge_pairs, bool reset) { for(auto cluster_charge_pair : isotopic_cluster_charge_pairs) setIsotopicCluster( cluster_charge_pair.first, cluster_charge_pair.second, reset); // qDebug() << qSetRealNumberPrecision(15) << "m_smallestMz:" << m_smallestMz //<< "m_greatestMz:" << m_greatestMz //<< "m_mostIntensePeakMz:" << m_mostIntensePeakMz; } /*! \brief Handles the \a isotopic_cluster_charge_pairs input isotopic cluster as a vector of IsotopicClusterChargePair instances. The member vector of \l IsotopicClusterChargePair instances is cleared before the computations. */ void IsotopicClusterShaper::setIsotopicClusterChargePairs( const std::vector &isotopic_cluster_charge_pairs) { setIsotopicClusterChargePairs(isotopic_cluster_charge_pairs, true); // qDebug() << qSetRealNumberPrecision(15) << "m_smallestMz:" << m_smallestMz //<< "m_greatestMz:" << m_greatestMz //<< "m_mostIntensePeakMz:" << m_mostIntensePeakMz; } /*! \brief Adds an isotopic cluster in the form of the \a isotopic_cluster pappso::Trace associated to the corresponding \a charge. The member vector of \l IsotopicClusterChargePair instances is \e{not} cleared before the computations. */ void IsotopicClusterShaper::appendIsotopicCluster( const pappso::Trace &isotopic_cluster, int charge) { // Do not clear the isotopic clusters! setIsotopicCluster(isotopic_cluster, charge, false); } /*! \brief Adds isotopic clusters in the form of the \a isotopic_cluster_charge_pairs vector of IsotopicClusterChargePair instances. The member vector of \l IsotopicClusterChargePair instances is \e{not} cleared before the computations. */ void IsotopicClusterShaper::appendIsotopicClusterChargePairs( const std::vector &isotopic_cluster_charge_pairs) { setIsotopicClusterChargePairs(isotopic_cluster_charge_pairs, false); // qDebug() << "m_smallestMz:" << m_smallestMz << "m_greatestMz:" << // m_greatestMz //<< "m_mostIntensePeakMz:" << m_mostIntensePeakMz; } /*! \brief Returns the final result of all the computations as a string. */ QString IsotopicClusterShaper::shapeToString() { return m_finalTrace.toString(); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicData.cpp000664 001750 001750 00000145414 15100504560 025363 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2024 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include #include /////////////////////// Qt includes #include /////////////////////// IsoSpec #include #include // extern const int elem_table_atomicNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int elem_table_massNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int // elem_table_extraNeutrons[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const bool elem_table_Radioactive[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_log_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicData \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile IsotopicData.hpp \brief The IsotopicData class provides a collection of \l{Isotope}s and associated methods to access them in various ways. The IsotopicData class provides a collection of \l{Isotope}s and provides methods to access them in various ways. Methods are available to return the monoisotopic mass of an isotope or the average mass calculated from the data of all the isotopes listed for a given chemical element. */ /*! \variable MsXpS::libXpertMassCore::IsotopicData::m_isotopes \brief The vector of \l{MsXpS::libXpertMassCore::IsotopeQSPtr}. The vector should never be sorted as we want to keep the order of the isotopes in the way the vector has been populated, either by looking into the IsoSpec library tables or by reading data from a user-configured file. */ /*! \variable MsXpS::libXpertMassCore::IsotopicData::m_symbolMonoMassMap \brief The map relating the Isotope::m_symbol to the monoisotopic mass. */ /*! \variable MsXpS::libXpertMassCore::IsotopicData::m_symbolAvgMassMap \brief The map relating the Isotope::m_symbol to the average mass. */ /*! \variable MsXpS::libXpertMassCore::IsotopicData::m_isValid \brief The validity status of the IsotopicData instance. \sa isValid(), validate() */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataSPtr \relates IsotopicData Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataCstSPtr \relates IsotopicData Synonym for std::shared_ptr. */ using IsotopeListCstIterator = QList::const_iterator; using IsotopeListIterator = QList::iterator; using IsotopeListCstIteratorPair = std::pair; /*! \typealias MsXpS::libXpertMassCore::IsotopeListCstIterator Alias for QList::const_iterator. */ /*! \typealias MsXpS::libXpertMassCore::IsotopeListIterator Alias for QList::iterator. */ /*! \typealias MsXpS::libXpertMassCore::IsotopeListCstIteratorPair Alias for std::pair. */ /*! \brief Constructs the \l{IsotopicData}. The instance will have empty member data. */ IsotopicData::IsotopicData(QObject *parent): QObject(parent) { } /*! \brief Constructs the \l{IsotopicData} as a copy of \a other. This is a deep copy with all the data in the containers copied from \a other to this IsotopicData. */ IsotopicData::IsotopicData(const IsotopicData &other, QObject *parent) : QObject(parent), m_isotopes(other.m_isotopes), m_symbolMonoMassMap(other.m_symbolMonoMassMap), m_symbolAvgMassMap(other.m_symbolAvgMassMap) { } /*! \brief Destructs the \l{IsotopicData}. Nothing is explicitely deleted in the destructor. */ IsotopicData::~IsotopicData() { // qDebug(); } /*! \brief Appends a new \l{IsotopeQSPtr} to this \l{IsotopicData}. \a isotope_qsp The new isotope to be added to this collection. The isotope is added to the end of the collection using \code m_isotopes.push_back(isotope_qsp); \endcode Each time a new isotope is added to this collection, the chemical signification of the corresponding chemical element changes at heart. It might thus be required that the data in the two m_symbolMonoMassMap and m_symbolAvgMassMap maps be recalculated. \a update_maps Tells if the m_symbolMonoMassMap and m_symbolAvgMassMap need to be updated with the new collection of isotopes. \sa updateMassMaps(), appendNewIsotopes() */ void IsotopicData::appendNewIsotope(IsotopeQSPtr isotope_qsp, bool update_maps) { // We may append an unvalid isotope, but then that changes the status // of these isotopic data. QVector error_list; m_isValid = isotope_qsp->validate(&error_list); m_isotopes.push_back(isotope_qsp); // We have modified the fundamental data, we may need to recompute some data. // update_maps might be false when loading data from a file, in which case it // is the responsibility of the user to call updateMassMaps() at the end of // the file loading. if(update_maps) updateMassMaps(); } /*! \brief Appends a collection of new \l{IsotopeQSPtr} to this \l{IsotopicData}. \a isotopes The collection () of new isotopes to be added to this collection. The isotope is added to the end of the collection using \code m_isotopes.insert(m_isotopes.end(), isotopes.begin(), isotopes.end()); \endcode Each time new isotopes are added to this collection, the chemical signification of all the corresponding chemical elements changes at heart. It might thus be required that the data in the two m_symbolMonoMassMap and m_symbolAvgMassMap maps be recalculated. Internally, this function calls .insert() to append all the isotopes in \a isotopes to the end of m_isotopes. \a update_maps Tells if the m_symbolMonoMassMap and m_symbolAvgMassMap need to be updated with the new collection of isotopes. \sa updateMassMaps(), appendNewIsotope() */ void IsotopicData::appendNewIsotopes(const QList &isotopes, bool update_maps) { qsizetype count_before = m_isotopes.size(); m_isotopes.append(isotopes.begin(), isotopes.end()); qsizetype count_after = m_isotopes.size(); if(count_after - count_before != isotopes.size()) qFatal("Programming error."); if(update_maps) updateMassMaps(); } /*! \brief Inserts a new \l{IsotopeQSPtr} to this \l{IsotopicData} at index \a index. \a isotope_qsp The new isotope to be inserted in this collection. If \a index is out of bounds or this collection is empty, the isotope is appended to this collection. Otherwise, the isotope is inserted at the requested index, which means that the new isotope displaces to the bottom (aka back) the isotope currently at \a index. Each time a new isotope is added to this collection, the chemical signification of the corresponding chemical element changes at heart. It might thus be required that the data in the two m_symbolMonoMassMap and m_symbolAvgMassMap maps be recalculated. \a update_maps Tells if the m_symbolMonoMassMap and m_symbolAvgMassMap need to be updated with the new collection of isotopes. Returns true if the iterator at the inserted position is not m_isotopes.end(). \sa updateMassMaps(), appendNewIsotope(), appendNewIsotopes() */ bool IsotopicData::insertNewIsotope(IsotopeQSPtr isotope_qsp, qsizetype index, bool update_maps) { // qDebug() << "the size of the data:" << size() << "and" << m_isotopes.size() //<< "requested index:" << index; // We define that we insert the new isotope before the one at index, as is the // convention in the STL and in Qt code. if(!m_isotopes.size() || index > m_isotopes.size() - 1) { appendNewIsotope(isotope_qsp, update_maps); return true; } // Convert the index to an iterator. QList::const_iterator iter = m_isotopes.begin() + index; // Finally, do the insertion. iter = m_isotopes.insert(iter, isotope_qsp); // qDebug() << "Inserted isotope:" << (*iter)->getSymbol(); // If inserting an empty isotope in relation to a row insertion in the table // view, then update_maps needs to be false because update_maps needs valid // symbols for isotopes! if(update_maps) updateMassMaps(); // iter points to the inserted isotope. return iter != m_isotopes.end(); } /*! \brief Removes the isotopes located between \a begin_index and \a end_index. The removed isotopes are contained inclusively between the two indices passed as parameters. Each time isotopes are removed from this collection, the chemical signification of the corresponding chemical elements changes at heart. It might thus be required that the data in the two m_symbolMonoMassMap and m_symbolAvgMassMap maps be recalculated. \a update_maps Tells if the m_symbolMonoMassMap and m_symbolAvgMassMap need to be updated with the new collection of isotopes. Returns an iterator to the end of this collection if either \a begin_index is out of bounds or this collection is empty. Otherwise, returns an iterator to the collection at the position below the last removed item. */ QList::const_iterator IsotopicData::eraseIsotopes(qsizetype begin_index, qsizetype end_index, bool update_maps) { // qDebug() << "Erasing isotopes in inclusive index range: [" << begin_index //<< "-" << end_index << "] - range is fully inclusive."; if(!m_isotopes.size() || begin_index > m_isotopes.size() - 1) return m_isotopes.cend(); QList::const_iterator iter_begin = m_isotopes.cbegin() + begin_index; // Let's say that by default, we remove until the last inclusively: QList::const_iterator iter_end = m_isotopes.cend(); // But, if end_index is less than the last index, then end() has to be the // next item after the one at that index. if(end_index < m_isotopes.size() - 1) iter_end = std::next(m_isotopes.begin() + end_index); // At this point we are confident we can assign the proper end() iterator // value for the erase function below. auto iter = m_isotopes.erase(iter_begin, iter_end); if(update_maps) { // qDebug() << "Now updating masses"; updateMassMaps(); } // else // qDebug() << "Not updating masses"; #if 0 if(m_isotopes.size()) { qDebug() << "The avg mass of the first isotope symbol in the vector:" << computeAvgMass( getIsotopesBySymbol(m_isotopes.front()->getSymbol())); } #endif return iter; } /*! \brief Redefines the monoisotopic mass of the chemical element specified by \a symbol. For the set of isotopes corresponding to \a symbol, set the most abundant isotope's mass as the value for key \a symbol in m_symbolMonoMassMap. Returns true if the map pair was actually inserted in m_symbolMonoMassMap or false if the monoisotopic mass value was set to an existing key. */ bool IsotopicData::updateMonoMassMap(const QString &symbol) { // We do only work with a single symbol here. // GCOVR_EXCL_START if(symbol.isEmpty()) qFatal("Programming error. The symbol cannot be empty."); // GCOVR_EXCL_STOP double greatest_abundance = std::numeric_limits::min(); double mass = 0.0; IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); while(iter_pair.first != iter_pair.second) { if((*iter_pair.first)->getProbability() > greatest_abundance) { greatest_abundance = (*iter_pair.first)->getProbability(); mass = (*iter_pair.first)->getMass(); } ++iter_pair.first; } // At this point we have the mono mass of the currently iterated symbol. bool key_found = m_symbolMonoMassMap.contains(symbol); m_symbolMonoMassMap.insert(symbol, mass); return key_found; } /*! \brief Redefines the monoisotopic mass of all the chemical elements in this collection of isotopes. This function is generally called by default by all the functions that add new isotopes to this collection [via updateMassMaps()]. First, a list of all the unique element symbols in this collection is crafted. Then for each symbol in that list, updateMonoMassMap(symbol) is called. Returns the number of updated symbols, that is, the unique symbol count in this collection. \sa updateMonoMassMap(const QString &symbol), updateMassMaps() */ std::size_t IsotopicData::updateMonoMassMap() { // For all the common chemical elements found in organic substances, the // monoisotopic mass is the mass of the most abundant isotope which // happens to be also the lightest isotope. However that is not true for // *all* the chemical elements. We thus need to iterate in the isotopes // of each symbol in the vector of isotopes and record the mass of the // isotope that is most abundant. m_symbolMonoMassMap.clear(); // Get the list of all the isotope symbols. std::size_t count = 0; std::vector all_symbols = getUniqueSymbolsInOriginalOrder(); for(auto symbol : all_symbols) { updateMonoMassMap(symbol); ++count; } return count; } /*! \brief Recalculates the average mass of the chemical element specified by \a symbol. For the set of isotopes corresponding to \a symbol, compute the average mass and set it in m_symbolAvgMassMap as the value for key \a symbol. Returns true if the map pair was actually inserted in m_symbolAvgMassMap or false if the average mass value was set to an already existing key. \sa updateMonoMassMap(const QString &symbol), updateMonoMassMap(), updateAvgMassMap(const QString &symbol), updateMassMaps() */ bool IsotopicData::updateAvgMassMap(const QString &symbol) { // For each chemical element (that is either name or symbol), we need to // compute the sum of the probabilities of all the corresponding // isotopes. Once that sum (which should be 1) is computed, it is // possible to compute the averag mass of "that symbol", so to say. // We do only work with a single symbol here. // GCOVR_EXCL_START if(symbol.isEmpty()) qFatal("Programming error. The symbol cannot be empty."); // GCOVR_EXCL_STOP #if 0 // Now this is in a function per se: // computeAvgMass(IsotopeListCstIteratorPair iter_pair, bool *ok) double cumulated_probabilities = 0.0; double avg_mass = 0.0; IsotopeListCstIteratorPair pair = getIsotopesBySymbol(symbol); // We need to use that iterator twice, so we do make a copy. IsotopeCstIterator local_iter = pair.first; while(local_iter != pair.second) { cumulated_probabilities += (*pair.first)->getProbability(); ++local_iter; } // Sanity check if(!cumulated_probabilities) qFatal("Programming error. The cumulated probabilities cannot be naught."); // And at this point we can compute the average mass. local_iter = pair.first; while(local_iter != pair.second) { avg_mass += (*local_iter)->getMass() * ((*local_iter)->getProbability() / cumulated_probabilities); ++local_iter; } #endif IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); // qDebug() << "For symbol" << symbol << "the iter range was found to // be of distance:" //<< std::distance(iter_pair.first, iter_pair.second) //<< "with symbol: " << (*iter_pair.first)->getSymbol(); ErrorList error_list; double avg_mass = computeAvgMass(iter_pair, &error_list); // GCOVR_EXCL_START if(error_list.size()) { qFatal( "The calculation of the average mass for a given " "symbol failed."); } // GCOVR_EXCL_STOP bool key_found = m_symbolAvgMassMap.contains(symbol); m_symbolAvgMassMap.insert(symbol, avg_mass); return key_found; } /*! \brief Recalculates the average mass of all the chemical elements in this collection of isotopes. This function is generally called by default by all the functions that add new isotopes to this collection [via updateMassMaps()]. First, a list of all the unique element symbols in this collection is crafted. Then for each symbol in that list, updateAvgMassMap(symbol) is called. Returns the number of updated symbols, that is, the unique symbol count in this collection. \sa updateMonoMassMap(const QString &symbol), updateMassMaps() */ std::size_t IsotopicData::updateAvgMassMap() { // For each chemical element (that is either name or symbol), we need to // compute the sum of the probabilities of all the corresponding // isotopes. Once that sum (which should be 1) is computed, it is // possible to compute the averag mass of "that symbol", so to say. m_symbolAvgMassMap.clear(); // Get the list of all the isotope symbols. std::size_t count = 0; std::vector all_symbols = getUniqueSymbolsInOriginalOrder(); for(auto symbol : all_symbols) { updateAvgMassMap(symbol); ++count; } return count; } /*! \brief Compute the average mass for isotopes contained in the \a iter_pair iterator range. \a iter_pair pair of [begin -- end[ iterators to the isotopes in this collection \a error_list_p vector of strings in which to store error messages There are no sanity checks performed. The iterator pair should hold two iterator values that frame isotopes of the same chemical element. The average mass is computed on the basis of the isotopes contained in the [\a iter_pair .first -- \a iter_pair .second[ range. Returns 0 if the first member of \a iter_pair is the collection's end iterator, the average mass otherwise. */ double IsotopicData::computeAvgMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const { // We get an iterator range for which we need to compute the average // mass. No check whatsoever, we do what we are asked to do. This // function is used to check or document user's actions in some places. double avg_mass = 0.0; // qDebug() << "Computing avg mass for iter range of distance:" //<< std::distance(iter_pair.first, iter_pair.second) //<< "with symbol: " << (*iter_pair.first)->getSymbol(); if(iter_pair.first == m_isotopes.cend()) { qDebug() << "First iterator is actually end of m_isotopes."; error_list_p->push_back( QString("First iterator is actually end of m_isotopes.")); return avg_mass; } qsizetype previous_error_count = error_list_p->size(); double cumulated_probabilities = getCumulatedProbabilities(iter_pair, error_list_p); if(error_list_p->size() > previous_error_count || !cumulated_probabilities) { // There was an error. We want to report it. error_list_p->push_back( QString("Failed to compute the cumulated probabilities needed to " "compute the average mass.")); return avg_mass; } // At this point, compute the average mass. while(iter_pair.first != iter_pair.second) { avg_mass += (*iter_pair.first)->getMass() * ((*iter_pair.first)->getProbability() / cumulated_probabilities); // qDebug() << "avg_mass:" << avg_mass; ++iter_pair.first; } return avg_mass; } /*! \brief Update the monoisotopic and average symbol-mass maps only for \a symbol. \sa updateMonoMassMap(const QString &symbol), updateAvgMassMap(const QString &symbol) */ void IsotopicData::updateMassMaps(const QString &symbol) { updateMonoMassMap(symbol); updateAvgMassMap(symbol); } /*! \brief Update the monoisotopic and average symbol-mass maps for all the symbols in the collection. This function is typically called each time new isotopes are added to this collection. Returns the count of updated symbols, that is, the unique symbol count in this collection. \sa updateMonoMassMap(), updateAvgMassMap() */ std::size_t IsotopicData::updateMassMaps() { std::size_t count_mono = updateMonoMassMap(); if(!count_mono) qDebug("There are no isotopes. Cleared the mono mass map."); std::size_t count_avg = updateAvgMassMap(); if(!count_avg) qDebug("There are no isotopes. Cleared the avg mass map."); if(count_mono != count_avg) qFatal("Programming error."); // Number of symbols for which the mass was updated. return count_mono; } /*! \brief Returns the monoisotopic mass for element of \a symbol. Returns 0 if \a symbol was not found in this Isotope collection and sets \a ok to false if \a ok is not nullptr; returns the monoisotopic mass for element \a symbol otherwise and sets \a ok to true if \a ok is not nullptr. */ double IsotopicData::getMonoMassBySymbol(const QString &symbol, bool &ok) const { if(symbol.isEmpty()) qFatal("Programming error. The symbol cannot be empty."); // qDebug() << "The symbol/mono mass map has size:" //<< m_symbolMonoMassMap.size(); QMap::const_iterator found_iter = m_symbolMonoMassMap.find(symbol); if(found_iter == m_symbolMonoMassMap.cend()) { qDebug() << "Failed to find the symbol in the map."; ok = false; return 0.0; } ok = true; // qDebug() << "The mono mass is found to be" << found_iter->second; return found_iter.value(); } /*! \brief Returns the mass of the most abundant isotope in a range of isotopes. The range of isotopes is defined by \a iter_pair, that is [ \a iter_pair .first -- \a iter_pair .second [. If errors are encountered, these are appended to \a error_list_p. For all the common chemical elements found in organic substances, the monoisotopic mass is the mass of the most abundant isotope which happens to be also the lightest isotope. However that is not true for *all* the chemical elements. We thus need to iterate in the isotopes of each symbol in the vector of isotopes and record the mass of the isotope that is most abundant. */ double IsotopicData::getMonoMass(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const { // The mono mass of a set of isotopes is the mass of the most abundant // isotope (not the lightest!). double mass = 0.0; double greatest_abundance = 0.0; if(iter_pair.first == m_isotopes.cend()) { qDebug() << "First iterator is actually end of m_isotopes."; error_list_p->push_back( QString("First iterator is actually end of m_isotopes.")); return mass; } while(iter_pair.first != iter_pair.second) { if((*iter_pair.first)->getProbability() > greatest_abundance) { greatest_abundance = (*iter_pair.first)->getProbability(); mass = (*iter_pair.first)->getMass(); } ++iter_pair.first; } // qDebug() << "The mono mass is found to be" << mass; return mass; } /*! \brief Returns the average mass of \a symbol. The returned mass is found as the value for key \a symbol in m_symbolAvgMassMap. If \a ok is not nullptr, it is set to true. If the symbol is not found, 0 is returned and \a ok is set to false if \a ok is not nullptr. */ double IsotopicData::getAvgMassBySymbol(const QString &symbol, bool &ok) const { if(symbol.isEmpty()) qFatal("Programming error. The symbol cannot be empty."); // qDebug() << "The symbol/avg mass map has size:" << // m_symbolAvgMassMap.size(); QMap::const_iterator found_iter = m_symbolAvgMassMap.find(symbol); if(found_iter == m_symbolAvgMassMap.cend()) { qDebug() << "Failed to find the symbol in the map."; ok = false; return 0.0; } ok = true; // qDebug() << "The avg mass is found to be" << found_iter->second; return found_iter.value(); } /*! \brief Returns the sum of the probabilities of all the isotopes of \a symbol. If errors occur, they will be described as strings appended in \a error_list_p. */ double IsotopicData::getCumulatedProbabilitiesBySymbol(const QString &symbol, ErrorList *error_list_p) const { // qDebug() << "symbol: " << symbol; double cumulated_probabilities = 0.0; // We'll need this to calculate the indices of the isotopes in the // m_isotopes vector. IsotopeListCstIterator iter_begin = m_isotopes.cbegin(); // Get the isotopes iter range for symbol. IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); while(iter_pair.first != iter_pair.second) { QString error_text; ErrorList iter_error_list; bool result = (*iter_pair.first)->validate(&iter_error_list); if(!result) { error_text = QString("Isotope symbol %1 at index %2:\n%3") .arg(symbol) .arg(std::distance(iter_begin, iter_pair.first)) .arg(Utils::joinErrorList(iter_error_list, "\n")); error_list_p->push_back(error_text); cumulated_probabilities = 0.0; } else cumulated_probabilities += (*iter_pair.first)->getProbability(); ++iter_pair.first; } return cumulated_probabilities; } /*! \brief Returns the sum of the probabilities of all the isotopes in the \a iter_pair range of iterators. The range of isotopes is defined by \a iter_pair, that is [ \a iter_pair .first -- \a iter_pair .second [. If errors are encountered, these are appended to \a error_list_p. */ double IsotopicData::getCumulatedProbabilities(IsotopeListCstIteratorPair iter_pair, ErrorList *error_list_p) const { double cumulated_probabilities = 0.0; if(iter_pair.first == m_isotopes.cend()) { qWarning() << "First iterator is actually end of m_isotopes."; error_list_p->push_back( QString("First iterator is actually end of m_isotopes.")); return cumulated_probabilities; } while(iter_pair.first != iter_pair.second) { cumulated_probabilities += (*iter_pair.first)->getProbability(); ++iter_pair.first; } // qDebug() << "cumulated_probabilities:" << cumulated_probabilities; return cumulated_probabilities; } /*! \brief Returns a range of iterators framing the isotopes of \a symbol. \note The order of the isotopes in the collection is not alphabetical (it is the order of the atomic number. This function works on the assumption that all the isotopes of a given symbol are clustered together in the isotopes vector with *no* gap in between. If \a symbol is empty, the iterators are set to be end() of the Isotopes collection. The returned pair of iterators frame the isotopes of \a symbol as a [begin,end[ pair of iterators. */ IsotopeListCstIteratorPair IsotopicData::getIsotopesBySymbol(const QString &symbol) const { if(symbol.isEmpty()) return IsotopeListCstIteratorPair(m_isotopes.cend(), m_isotopes.cend()); // qDebug() << "The symbol by which isotopes are being searched for: " << // symbol; // We want to "extract" from the vector of isotopes, the ones that share // the same symbol under the form of a [begin,end[ pair of iterators. //////////////////////////// ASSUMPTION ///////////////////////// //////////////////////////// ASSUMPTION ///////////////////////// // The order of the isotopes is not alphabetical (it is the order of the // atomic number. This function works on the assumption that all the // isotopes of a given symbol are clustered together in the isotopes // vector with *no* gap in between. //////////////////////////// ASSUMPTION ///////////////////////// //////////////////////////// ASSUMPTION ///////////////////////// // We will start iterating in the vector of isotopes at the very // beginning. QList::const_iterator iter = m_isotopes.cbegin(); // Never reach the end of the isotopes vector. QList::const_iterator iter_end = m_isotopes.cend(); // This iterator will be the end iterator of the range that comprises // the isotopes all sharing the same symbol. We initialize it to // iter_end in case we do not find the symbol at all. Otherwise it will // be set to the right value. QList::const_iterator symbol_iter_end = iter_end; while(iter != iter_end) { QString current_symbol = (*iter)->getSymbol(); // qDebug() << "First loop iteration in isotope with symbol:" // << current_symbol; if(current_symbol != symbol) { // qDebug() << "Current isotope has symbol" << current_symbol //<< "and we are looking for" << symbol //<< "incrementing to next position."; ++iter; } else { // qDebug() << "Current isotope has symbol" << current_symbol //<< "and we are looking for" << symbol //<< "with mass:" << (*iter)->getMass() //<< "Now starting inner iteration loop."; // At this point we encountered one isotope that has the right // symbol. The iterator "iter" will not change anymore because // of the inner loop below that will go on iterating in vector // using another set of iterators. "iter" will thus point // correctly to the first isotope in the vector having the right // symbol. // Now in this inner loop, continue iterating in the vector, // starting at the present position and go on as long as the // encountered isotopes have the same symbol. // Set then end iterator to the current position and increment // to the next one, since current position has been iterated // into already (position is stored in "iter") and go on. This // way, if there was a single isotope by given symbol, // "symbol_iter_end" rightly positions at the next isotope. If // that is not the case, its value updates and is automatically // set to the first isotope that has not the right symbol (or // will be set to iter_end if that was the last set of isotopes // in the vector). symbol_iter_end = iter; ++symbol_iter_end; while(symbol_iter_end != iter_end) { // qDebug() << "Second loop iteration in: " //<< (*symbol_iter_end)->getSymbol() //<< "while we search for" << symbol //<< "Iterated isotope has mass:" //<< (*symbol_iter_end)->getMass(); if((*symbol_iter_end)->getSymbol() == symbol) { // We can iterate further in the isotopes vector because // the current iterator pointed to an isotope that still // had the right symbol. qDebug() //<< "Good symbol, going to next inner iteration // position."; ++symbol_iter_end; } else { // We currently iterate in an isotope that has a symbol // different from the searched one: the symbol_iter_end // thus effectively plays the role of the // iterator::end() of the isotopes range having the // proper symbol. // qDebug() << "The symbols do not match, breaking the inner // loop."; break; } } // We can break the outer loop because we have necessarily gone // through the isotopes of the requested symbol at this point. // See at the top of this outer loop that when an isotope has // not the right symbol, the iter is incremented. break; } // End of block // else of if(current_symbol != symbol) } // End of outer // while(iter != iter_end) // qDebug() << "For symbol" << symbol << "the iter range was found to be:" //<< std::distance(iter, symbol_iter_end); return IsotopeListCstIteratorPair(iter, symbol_iter_end); } /*! \brief Returns the count of isotopes of \a symbol. */ qsizetype IsotopicData::getIsotopeCountBySymbol(const QString &symbol) const { IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); return std::distance(iter_pair.first, iter_pair.second); } /*! \brief Returns a range of iterators framing the isotopes of element \a name. \note The order of the isotopes in the collection is not alphabetical (it is the order of the atomic number. This function works on the assumption that all the isotopes of a given symbol are clustered together in the isotopes vector with *no* gap in between. If \a name is empty, the iterators are set to be end() of the Isotopes collection. The returned pair of iterators frame the isotopes of \a name as a [begin,end[ pair of iterators. */ IsotopeListCstIteratorPair IsotopicData::getIsotopesByName(const QString &name) const { if(name.isEmpty()) return IsotopeListCstIteratorPair(m_isotopes.cend(), m_isotopes.cend()); // qDebug() << "The name by which isotopes are being searched for: " << name; // We want to "extract" from the vector of isotopes, the ones that share // the same name under the form of a [begin,end[ pair of iterators. //////////////////////////// ASSUMPTION ///////////////////////// //////////////////////////// ASSUMPTION ///////////////////////// // The order of the isotopes is not alphabetical (it is the order of the // atomic number. This function works on the assumption that all the // isotopes of a given symbol are clustered together in the isotopes // vector with *no* gap in between. //////////////////////////// ASSUMPTION ///////////////////////// //////////////////////////// ASSUMPTION ///////////////////////// // We will start iterating in the vector of isotopes at the very // beginning. QList::const_iterator iter = m_isotopes.cbegin(); // Never reach the end of the isotopes vector. QList::const_iterator iter_end = m_isotopes.cend(); // This iterator will be the end iterator of the range that comprises // the isotopes all sharing the same name. We initialize it to iter_end // in case we do not find the name at all. Otherwise it will be set to // the right value. QList::const_iterator name_iter_end = iter_end; while(iter != iter_end) { QString current_name = (*iter)->getName(); // qDebug() << "First loop iteration in isotope with name:" << // current_name; if(current_name != name) { // qDebug() << "Current isotope has name" << current_name //<< "and we are looking for" << name //<< "incrementing to next position."; ++iter; } else { // qDebug() << "Current isotope has name" << current_name //<< "and we are looking for" << name //<< "with mass:" << (*iter)->getMass() //<< "Now starting inner iteration loop."; // At this point we encountered one isotope that has the right name. // The iterator "iter" will not change anymore because of the inner // loop below that will go on iterating in vector using another set // of iterators. "iter" will thus point correctly to the first // isotope in the vector having the right name. // Now in this inner loop, continue iterating in the vector, // starting at the present position and go on as long as the // encountered isotopes have the same name. // Set then end iterator to the current position and increment to // the next one, since current position has been iterated into // already (position is stored in "iter") and go on. This way, if // there was a single isotope by given name, "name_iter_end" rightly // positions at the next isotope. If that is not the case, its value // updates and is automatically set to the first isotope that has // not the right name (or will be set to iter_end if that was the // last set of isotopes in the vector). name_iter_end = iter; ++name_iter_end; while(name_iter_end != iter_end) { // qDebug() << "Second loop iteration in: " //<< (*name_iter_end)->getName() //<< "while we search for" << name //<< "Iterated isotope has mass:" //<< (*name_iter_end)->getMass(); if((*name_iter_end)->getName() == name) { // We can iterate further in the isotopes vector because the // current iterator pointed to an isotope that still had the // right name. // qDebug() << "Going to next iterator position."; ++name_iter_end; } else { // We currently iterate in an isotope that has a name // different from the searched one: the name_iter_end thus // effectively plays the role of the iterator::end() of the // isotopes range having the proper name. // qDebug() << "The names do not match, breaking the inner // loop."; break; } } // We can break the outer loop because we have necessarily gone // through the isotopes of the requested name at this point. // See at the top of this outer loop that when an isotope has // not the right name, the iter is incremented. break; } // End of block // else of if(current_name != name) } // End of outer // while(iter != iter_end) // qDebug() << "For name" << name << "the iter range was found to be:" //<< std::distance(iter, name_iter_end); return IsotopeListCstIteratorPair(iter, name_iter_end); } /*! \brief Returns all the unique symbols from the collection as they are stored. */ std::vector IsotopicData::getUniqueSymbolsInOriginalOrder() const { // The way IsoSpec works and the way we configure the // symbol/count/isotopes data depend in some situations on the order in // which the Isotopes were read either from the library tables or from // user-created disk files. // // This function wants to craft a list of unique isotope symbols exactly // as they are found in the member vector. std::set symbol_set; std::vector symbols; for(auto isotope_qsp : m_isotopes) { QString symbol = isotope_qsp->getSymbol(); auto res = symbol_set.insert(symbol); // res.second is true if the symbol was inserted, which mean it did // not exist in the set. if(res.second) symbols.push_back(symbol); } return symbols; } /*! \brief Returns true if the collection contains at least one isotope of \a symbol, false otherwise. If \a count is not nullptr, its value is set to the count of isotopes of \a symbol or unchanged if no isotopes by \a symbol were found. */ bool IsotopicData::containsSymbol(const QString &symbol, int &count) const { IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); if(iter_pair.first == m_isotopes.cend()) return false; // Now compute the distance between the two iterators to know how many // isotopes share the same chemical element symbol. count = std::distance(iter_pair.first, iter_pair.second); return true; } /*! \brief Returns true if the collection contains at least one isotope of element \a name, false otherwise. If \a count is not nullptr, its value is set to the count of isotopes of \a name element. */ bool IsotopicData::containsName(const QString &name, int &count) const { IsotopeListCstIteratorPair iter_pair = getIsotopesByName(name); if(iter_pair.first == m_isotopes.cend()) return false; // Now compute the distance between the two iterators to know how many // isotopes share the same chemical element symbol. count = std::distance(iter_pair.first, iter_pair.second); return true; } /*! \brief Returns a string containing a stanza describing each isotope of \a symbol. \sa Isotope::toString, Isotope::getMass */ QString IsotopicData::isotopesAsStringBySymbol(const QString &symbol) const { if(symbol.isEmpty()) qFatal("Programming error. The symbol cannot be empty."); QString text; IsotopeListCstIteratorPair iter_pair = getIsotopesBySymbol(symbol); if(iter_pair.first == m_isotopes.cend()) { qDebug() << "The symbol was not found in the vector of isotopes."; return text; } while(iter_pair.first != iter_pair.second) { text += (*iter_pair.first)->toString(); text += "\n"; } return text; } /*! \brief Returns true if the \a isotope_cqsp is isotope of its symbol having the greatest probability, false otherwise. */ bool IsotopicData::isMonoMassIsotope(IsotopeCstQSPtr isotope_cqsp) { // Is the passed isotope the one that has the greatest abundance among // all the isotopes having the same symbol. if(isotope_cqsp == nullptr) qFatalStream() << "Programming error. The isotope pointer cannot be nullptr."; QString symbol(isotope_cqsp->getSymbol()); if(!m_symbolMonoMassMap.contains(symbol)) qFatalStream() << "Programming error. The symbol was not found in the map."; double mass = m_symbolMonoMassMap.value(symbol); if(mass == isotope_cqsp->getMass()) return true; return false; } /*! \brief Returns a constant reference to the container of \l{MsXpS::libXpertMassCore::Isotope}s. */ const QList & IsotopicData::getIsotopes() const { return m_isotopes; } /*! \brief Assigns member data from \a other to this instance's member data. The copying of \a other into this collection is deep, making *this collection essentially identical to \a other. Returns a reference to this collection. */ IsotopicData & IsotopicData::operator=(const IsotopicData &other) { if(&other == this) return *this; m_isotopes = other.m_isotopes; m_symbolMonoMassMap = other.m_symbolMonoMassMap; m_symbolAvgMassMap = other.m_symbolAvgMassMap; return *this; } /*! \brief Returns true if \a other and \c this are identical. */ bool IsotopicData::operator==(const IsotopicData &other) const { if(&other == this) return true; if(m_isotopes.size() != other.m_isotopes.size()) return false; for(qsizetype iter = 0; iter < m_isotopes.size(); ++iter) { // qDebug() << "Now checking Isotope" << m_isotopes.at(iter)->getName(); if(*m_isotopes.at(iter) != *other.m_isotopes.at(iter)) { qWarning() << "At least one isotope differs in each data set."; return false; } } // qDebug() << "Done checking the isotopes."; if(m_symbolMonoMassMap != other.m_symbolMonoMassMap) return false; if(m_symbolAvgMassMap != other.m_symbolAvgMassMap) return false; // qDebug() << "The isotopic data are identical."; return true; } /*! \brief Returns true if \a other and \c this are different. */ bool IsotopicData::operator!=(const IsotopicData &other) const { if(&other == this) return false; return !operator==(other); } /*! \brief Returns the count of isotopes in the collection. \note The count that is returned is for \e all isotopes, that is, the count if items in the collection. */ qsizetype IsotopicData::size() const { return m_isotopes.size(); } /*! \brief Returns the validity status of this IsotopicData instance. \sa validate(), validateBySymbol(), validateAllBySymbol() */ bool IsotopicData::isValid() { return m_isValid; } /*! \brief Returns the count of unique symbols. */ qsizetype IsotopicData::getUniqueSymbolsCount() const { // Go trough the vector of IsotopeQSPtr and check how many different // symbols it contains. std::set symbols_set; QList::const_iterator iter = m_isotopes.cbegin(); QList::const_iterator iter_end = m_isotopes.cend(); while(iter != iter_end) { symbols_set.insert((*iter)->getSymbol()); ++iter; } // Remove these checks because in fact it is possible that the values // be different: if appending new isotopes without forcing the udpate // of the maps. // Sanity checks: // if(symbols_set.size() != m_symbolAvgMassMap.size()) // qFatal("Not possible that the sizes (avg) do not match"); // // if(symbols_set.size() != m_symbolMonoMassMap.size()) // qFatal("Not possible that the sizes (mono) do not match"); return symbols_set.size(); } /*! \brief Validates this Isotope collection. The validation involves iterating in the whole collection and for each item in it invoke its Isotope::validate(). If errors occurred during these validations, they are reported as strings in \a error_list_p. Return true if no error was encountered, false otherwise. */ bool IsotopicData::validate(ErrorList *error_list_p) const { int error_count = 0; IsotopeListCstIterator iter_begin = m_isotopes.cbegin(); IsotopeListCstIterator iter_end = m_isotopes.cend(); IsotopeListCstIterator iter = iter_begin; while(iter != iter_end) { QString error_text = ""; ErrorList isotope_error_list; bool result = (*iter)->validate(&isotope_error_list); if(!result) { error_text.append(QString("Isotope at index %1:\n") .arg(std::distance(iter_begin, iter))); error_text.append(Utils::joinErrorList(isotope_error_list, "\n")); error_list_p->push_back(error_text); ++error_count; } ++iter; } if(error_count) m_isValid = false; else m_isValid = true; return m_isValid; } /*! \brief Validates the \l{Isotope}s of \a symbol in this collection. This function is more powerful than IsotopicData::validate() because it actually verifies the integrity of the data \e{symbol}-wise. For example, a set of isotopes by the same symbol cannot have a cumulated probability different than 1. If that were the case, the error would be reported. Encountered errors are stored in \a error_list_p. Returns true if validation succeeded, false otherwise. \sa validateAllBySymbol(), getCumulatedProbabilitiesBySymbol() */ bool IsotopicData::validateBySymbol(const QString &symbol, ErrorList *error_list_p) const { // This function is more powerful than the other one because it actually // looks the integrity of the data symbol-wise. For example, a set of // isotopes by the same symbol cannot have a cumulated probability greater // than 1. If that were the case, that would be reported. // qDebug() << "symbol: " << symbol; // Validating by symbol means looking into each isotope that has the same // 'symbol' and validating each one separately. However, it also means looking // if all the cumulated isotope probabilities (abundances) for a given symbol // are different than 1. // Record the size of the error_list so that we can report if we added // errors in this block. qsizetype previous_error_count = error_list_p->size(); // The function below performs validation of the isotopes. double cumulated_probabilities = getCumulatedProbabilitiesBySymbol(symbol, error_list_p); if(cumulated_probabilities != 1) { QString prob_error = QString( "Isotope symbol %1: has cumulated probabilities not equal to 1.\n") .arg(symbol); error_list_p->push_back(prob_error); } // If we added errors, then return false. return !(error_list_p->size() > previous_error_count); } /*! \brief Validates all the isotopes of the collection. The validation of the \l{Isotope}s is performed by grouping them by symbol. See validateBySymbol(). Encountered errors are stored in \a error_list_p. Returns true if all the Isotopes validated successfully, false otherwise. \sa validateBySymbol(), validate(), getCumulatedProbabilitiesBySymbol() */ bool IsotopicData::validateAllBySymbol(ErrorList *error_list_p) const { // This validation of all the isotopes as grouped by symbol is more // informative than the validation isotope by isotope idependently // because it can spot cumulated probabilities problems. qsizetype previous_error_count = error_list_p->size(); std::vector all_symbols = getUniqueSymbolsInOriginalOrder(); for(auto symbol : all_symbols) { QString error_text = ""; validateBySymbol(symbol, error_list_p); } // If we added errors, then return false. return !(error_list_p->size() > previous_error_count); } /*! \brief Replaces \a old_isotope_sp by \a new_isotope_sp in this collection. */ void IsotopicData::replace(IsotopeQSPtr old_isotope_sp, IsotopeQSPtr new_isotope_sp) { std::replace( m_isotopes.begin(), m_isotopes.end(), old_isotope_sp, new_isotope_sp); } /*! \brief Clears (empties) the symbol/mass maps only, not the vector if Isotope instances. These two containers are cleared: \list \li m_symbolMonoMassMap \li m_symbolAvgMassMap \endlist */ void IsotopicData::clearSymbolMassMaps() { m_symbolMonoMassMap.clear(); m_symbolAvgMassMap.clear(); } /*! \brief Clears (empties) all the containers in this collection, essentially resetting it completely. These three containers are cleared: \list \li m_isotopes \li m_symbolMonoMassMap \li m_symbolAvgMassMap \endlist \sa clearSymbolMassMaps() */ void IsotopicData::clear() { m_isotopes.clear(); clearSymbolMassMaps(); } void IsotopicData::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register IsotopicData class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&IsotopicData::staticMetaObject); engine->globalObject().setProperty("IsotopicData", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS #if 0 Example from IsoSpec. const int elementNumber = 2; const int isotopeNumbers[2] = {2,3}; const int atomCounts[2] = {2,1}; const double hydrogen_masses[2] = {1.00782503207, 2.0141017778}; const double oxygen_masses[3] = {15.99491461956, 16.99913170, 17.9991610}; const double* isotope_masses[2] = {hydrogen_masses, oxygen_masses}; const double hydrogen_probs[2] = {0.5, 0.5}; const double oxygen_probs[3] = {0.5, 0.3, 0.2}; const double* probs[2] = {hydrogen_probs, oxygen_probs}; IsoLayeredGenerator iso(Iso(elementNumber, isotopeNumbers, atomCounts, isotope_masses, probs), 0.99); #endif libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicDataBaseHandler.cpp000664 001750 001750 00000011053 15100504560 027443 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include /////////////////////// IsoSpec #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicDataBaseHandler \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile IsotopicDataBaseHandler.hpp \brief The IsotopicDataBaseHandler class features basic handling of \l{IsotopicData}. The IsotopicDataBaseHandler class provides the skeleton for derived classes to handle \l{IsotopicData}. There are different isotopic data handlers: \list \li The \l{IsotopicDataLibraryHandler} that handles isotopic data from the IsoSpec element data tables directly from the library's data. These are the reference, pristine \e{unmodified} isotopic data. \li The \l{IsotopicDataUserConfigHandler} that handles isotopic data with the exact same format of those from the IsoSpec element data tables. However, these data correspond to user-modified isotopic data, \e{not} the reference, pristine \e{unmodified} isotopic data. \li The \l{IsotopicDataManualConfigHandler} that handles user-defined data according to an entirely different format. These data are typically used when the user defines new chemical elements that cannot fit in the IsoSpec element data tables format. \endlist \sa IsotopicDataLibraryHandler, IsotopicDataUserConfigHandler, IsotopicDataManualConfigHandler */ /*! \variable int MsXpS::libXpertMassCore::IsotopicDataBaseHandler::msp_isotopicData \brief The msp_isotopicData is a pointer to \l{IsotopicData}. */ /*! \variable int MsXpS::libXpertMassCore::IsotopicDataBaseHandler::m_fileName \brief The m_fileName is the filename in which to store the \l{IsotopicData} for from with to load the \l{IsotopicData}. */ /*! \brief Constructs the \l{IsotopicDataBaseHandler} with \a file_name. The msp_isotopicData is initialized by creating an empty IsotopicData instance. */ IsotopicDataBaseHandler::IsotopicDataBaseHandler(const QString &file_name) : m_fileName(file_name) { // We cannot allow to have a nullptr msp_isotopicData member. msp_isotopicData = std::make_shared(); } /*! \brief Constructs the \l{IsotopicDataBaseHandler}. \a isotopic_data_sp Isotopic data \a file_name File name */ IsotopicDataBaseHandler::IsotopicDataBaseHandler( IsotopicDataSPtr isotopic_data_sp, const QString &file_name) : msp_isotopicData(isotopic_data_sp), m_fileName(file_name) { // We cannot allow to have a nullptr msp_isotopicData member. if(msp_isotopicData == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; } /*! \brief Destructs the \l{IsotopicDataBaseHandler}. */ IsotopicDataBaseHandler::~IsotopicDataBaseHandler() { // qDebug(); } /*! \brief Returns the IsotopicData */ IsotopicDataSPtr IsotopicDataBaseHandler::getIsotopicData() { return msp_isotopicData; } /*! \brief Sets the file name to \a file_name */ void IsotopicDataBaseHandler::setFileName(const QString &file_name) { m_fileName = file_name; } /*! \brief Returns the file name */ QString IsotopicDataBaseHandler::getFileName() { return m_fileName; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicDataLibraryHandler.cpp000664 001750 001750 00000032613 15100504560 030202 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include /////////////////////// IsoSpec #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicDataLibraryHandler \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile IsotopicDataLibraryHandler.hpp \brief The IsotopicDataLibraryHandler class handles \l{IsotopicData} from the IsoSpec element data tables directly from the library's data. These are the reference, pristine, \e{unmodified}, isotopic data. The IsoSpec element data tables that are used in libXpertMassCore are the following: \list \li elem_table_element \li elem_table_symbol \li elem_table_mass \li elem_table_probability \endlist The data tables are all of the same length and the data in each row of a given table matches the contents of that same row in all the other tables. For example, the first two rows of table elem_table_ID are: 1 1 These two rows match the same rows in elem_table_mass: 1.00782503227 2.01410177819 and the the same rows in elem_table_element: "hydrogen" "hydrogen" By reading, row-by-row, the data from the same row number in each one of the tables, one constructs a fully qualified \l{Isotope}. \sa IsotopicDataUserConfigHandler, IsotopicDataManualConfigHandler */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataLibraryHandlerSPtr \relates IsotopicDataLibraryHandler Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataLibraryHandlerCstSPtr \relates IsotopicDataLibraryHandler Synonym for std::shared_ptr. */ /*! \brief Constructs the \l{IsotopicDataLibraryHandler}. The instance will have empty member data. */ IsotopicDataLibraryHandler::IsotopicDataLibraryHandler() : IsotopicDataBaseHandler() { } /*! \brief Constructs the \l{IsotopicDataLibraryHandler}. The instance will have its isotopic data member pointing to \a isotopic_data_sp. */ IsotopicDataLibraryHandler::IsotopicDataLibraryHandler( IsotopicDataSPtr isotopic_data_sp) : IsotopicDataBaseHandler(isotopic_data_sp) { } /*! \brief Destructs the \l{IsotopicDataLibraryHandler}. Nothing is explicitely deleted in the destructor. */ IsotopicDataLibraryHandler::~IsotopicDataLibraryHandler() { // qDebug(); } // NOT documented on purpose. // GCOV_EXCL_START qsizetype IsotopicDataLibraryHandler::loadData([[maybe_unused]] const QString &filename) { qsizetype count_non_isotope_skipped_items = 0; qsizetype count = loadData(count_non_isotope_skipped_items); if(count) qCritical() << "Not a single Isotope could be loaded from file."; if(count_non_isotope_skipped_items) qInfo() << "There were " << count_non_isotope_skipped_items << "non-Isotope items in the tables."; return count; } // GCOV_EXCL_STOP /*! \brief Loads isotopic data directly from IsoSpec library' element data tables. The member isotopic data are cleared before setting new data read from the library's element data tables. The code iterates, row-by-row, in the all the tables and extracts the data to fill in the Isotope data: \code IsotopeQSPtr isotope_qsp = std::make_shared(QString(IsoSpec::elem_table_element[iter]), QString(IsoSpec::elem_table_symbol[iter]), IsoSpec::elem_table_mass[iter], IsoSpec::elem_table_probability[iter]); \endcode Note that some rows in the table are populated with non-isotope data, like electron, missing_electron or protonation (twice). When these rows are encountered, the \a count_non_isotope_skipped_items is incremented. Returns the count of \l{Isotope}s that were allocated and stored in the msp_isotopicData member. */ qsizetype IsotopicDataLibraryHandler::loadData(qsizetype &count_non_isotope_skipped_items) { // We need to allocate one Isotope instance for each element // in the various arrays in the IsoSpec++ source code header file. // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // Big sanity check, all the arrays must be the same length! std::size_t array_length = checkConsistency(); if(array_length < 1) return false; // qDebug() << "The size of the tables:" << array_length; // Clear all the data, since this function might be called multiple times. msp_isotopicData->clear(); std::size_t loaded_isotopes = 0; for(std::size_t iter = 0; iter < array_length; ++iter) { QString elem_element = QString(IsoSpec::elem_table_element[iter]); // These are the last items in the various tables. We do not handle them // at the moment. if(elem_element == "electron" || elem_element == "missing electron" || elem_element == "protonation" /* occurs twice */) { ++count_non_isotope_skipped_items; continue; } IsotopeQSPtr isotope_qsp = QSharedPointer::create( QString(IsoSpec::elem_table_element[iter]), QString(IsoSpec::elem_table_symbol[iter]), IsoSpec::elem_table_mass[iter], IsoSpec::elem_table_probability[iter]); // We do not want to update the mono/avg maps each time we load an // isotope. We'll call the relevant function later. msp_isotopicData->appendNewIsotope(isotope_qsp, /* update maps*/ false); ++loaded_isotopes; } // Sanity check if((loaded_isotopes + count_non_isotope_skipped_items) != IsoSpec::isospec_number_of_isotopic_entries) qFatal( "Programming error. Error loading the isotopic data from IsoSpec++'s " "tables."); // Now ask that the mono/avg mass maps be updated. if(!msp_isotopicData->updateMassMaps()) qFatal("Programming error. Failed to update the mass maps."); // qDebug() << "Done loading data with :" << msp_isotopicData->size() // << "isotopes in the isotopic data."; return msp_isotopicData->size(); } /*! \brief Write all the IsotopicData to \a file_name. If \a file_name is empty, m_fileName is tried. If both are empty, the function returns 0. If any one of the file names are correct (file_name takes precedence over m_fileName), then m_fileName is set to that file name. The format of the file consists in a single line of data per \l{Isotope} as created using the Isotope::toString() function and Isotope::Isotope(const QString &text, QObject *parent). Each isotope is output to its own line. Returns the count of \l{Isotope::Isotope}s written to file or 0 if the file does not exist or is not readable. \sa Isotope::Isotope and overloads */ qsizetype IsotopicDataLibraryHandler::writeData(const QString &file_name) { // Although the isotopic data were loaded from the IsoSpec library tables, we // might be willing to store these data to a file. if(file_name.isEmpty() && m_fileName.isEmpty()) return 0; QString temp_file_name; // The passed filename takes precedence over the member datum. So copy // that file name to the member datum. if(!file_name.isEmpty()) temp_file_name = file_name; else temp_file_name = m_fileName; QFile file(temp_file_name); // qDebug() << "File name to write to:" << temp_file_name; if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qDebug("Failed to open file for writing."); return 0; } QTextStream out(&file); out << "# This file contains isotopic data in a format that can accommodate\n"; out << "# comments in the form of lines beginning with the '#' character.\n\n"; std::size_t isotope_count = 0; for(auto isotope_qsp : msp_isotopicData->m_isotopes) { out << isotope_qsp->toString(); // We need to add it because toString() does not terminate the line with // a new line character. out << "\n"; ++isotope_count; } out.flush(); file.close(); // Now we know that temp_file_name is fine. Store into m_fileName. m_fileName = temp_file_name; return isotope_count; } /*! \brief Checks the consistency in all the IsoSpec library's different isotopic data tables. This function essentially verifies that each table has the same row count as all the other ones. Returns the count of isotopes in the isotopic data. */ qsizetype IsotopicDataLibraryHandler::checkConsistency() { qsizetype array_length = sizeof(IsoSpec::elem_table_atomicNo) / sizeof(IsoSpec::elem_table_atomicNo[0]); // qDebug() << "The array length is:" << array_length; // All the tables in the header file of the IsoSpec library must // have exactly the same size. if((qsizetype)IsoSpec::isospec_number_of_isotopic_entries != array_length) { qFatal( "Found corruption: the size of IsoSpec arrays is not like expected."); } // Now test each table one by one. qsizetype tested_length = sizeof(IsoSpec::elem_table_probability) / sizeof(IsoSpec::elem_table_probability[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_mass) / sizeof(IsoSpec::elem_table_mass[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_massNo) / sizeof(IsoSpec::elem_table_massNo[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_extraNeutrons) / sizeof(IsoSpec::elem_table_extraNeutrons[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_element) / sizeof(IsoSpec::elem_table_element[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_symbol) / sizeof(IsoSpec::elem_table_symbol[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_Radioactive) / sizeof(IsoSpec::elem_table_Radioactive[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } tested_length = sizeof(IsoSpec::elem_table_log_probability) / sizeof(IsoSpec::elem_table_log_probability[0]); if(tested_length != array_length) { qDebug() << "Found corruption: at least two arrays are not of the same length." << "tested_length:" << tested_length; return 0; } return tested_length; } } // namespace libXpertMassCore } // namespace MsXpS #if 0 Example from IsoSpec. const int elementNumber = 2; const int isotopeNumbers[2] = {2,3}; const int atomCounts[2] = {2,1}; const double hydrogen_masses[2] = {1.00782503207, 2.0141017778}; const double oxygen_masses[3] = {15.99491461956, 16.99913170, 17.9991610}; const double* isotope_masses[2] = {hydrogen_masses, oxygen_masses}; const double hydrogen_probs[2] = {0.5, 0.5}; const double oxygen_probs[3] = {0.5, 0.3, 0.2}; const double* probs[2] = {hydrogen_probs, oxygen_probs}; IsoLayeredGenerator iso(Iso(elementNumber, isotopeNumbers, atomCounts, isotope_masses, probs), 0.99); #endif libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicDataManualConfigHandler.cpp000664 001750 001750 00000064341 15100504560 031144 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicDataManualConfigHandler \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile IsotopicDataManualConfigHandler.hpp \brief The IsotopicDataManualConfigHandler class handles a peculiar kind of \l{IsotopicData} that cannot be handled with the other handlers. This kind of IsotopicData handler is typically used when definition of brand new chemical element isotopes are to be performed. For example, if one works with radioactive carbon C14, then that isotope is not available in the IsoSpec's library and cannot be inserted in these data using the \l{IsotopicDataUserConfigHandler}. One interesting feature of this kind of IsotopicData formalism is that these data collectively describe a chemical elemental composition formula, like, for glucose: C6H12O6. Each chemical element is decribed using isotopes with mass/abundance pairs. There is thus virtually no limitation on the complexity of the isotopic distribution to be defined. The format of these data stored on file is nothing like the format used to store Library- or User-Config- isotopic data (\l{IsotopicDataLibraryHandler}, \l{IsotopicDataUserConfigHandler}). In the example below we are defining the isotopic composition of radiolabelled glucose (C6H12O6) where only two of the the six C atoms are 14[C] atoms with a radiolabelling efficiency of 95%: \code [Element] symbol C count 4 # The four atoms that are not labelled appear with natural # abundances for both stable isotopes [Isotopes] 2 mass 12.0 prob 0.989 mass 13.003354 prob 0.010788 [Element] # The two atoms that are 95% labelled with 14[C] need to use # a new artificial symbol symbol Cx count 2 [Isotopes] 3 # 5% of non labelled atoms are of natural 12[C] abundance mass 12.000 prob 0.05*0.989211941850466 # 5% of non labelled atoms are of natural 13[C] abundance mass 13.0033548352 prob 0.05*0.010788058149533084 # The remaining 95% of the atoms are 14[C] atoms with abundance 100% mass 14.003241989 prob 0.95*1.00 [Element] symbol H count 12 [Isotopes] 2 mass 1.0078250 prob 0.99988 mass 2.01410177 prob 0.00011570 [Element] symbol O count 6 [Isotopes] 3 mass 15.9949 prob 0.99756 mass 16.999 prob 0.000380 mass 17.999 prob 0.002051 \endcode Comments are allowed and are on lines that have as their first non-space character the '#' character. These lines are ignored when loading data. The data can be loaded from and written to file. \sa IsotopicDataLibraryHandler, IsotopicDataUserConfigHandler */ /*! \typealias MsXpS::libXpertMassCore::SymbolCountMap Alias for to std::map. */ /*! \variable MsXpS::libXpertMassCore::IsotopicDataManualConfigHandler::m_symbolCountMap Holds symbol/count pairs to document the count of any symbol in the isotopic data. This symbol count is stored in the file using the following format: \code [Element] symbol C count 6 \endcode The number of carbon atoms in the formula being defined is 6, as in glucose: C6H1206. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataManualConfigHandlerSPtr \relates IsotopicDataManualConfigHandler Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::IsotopicDataManualConfigHandlerCstSPtr \relates IsotopicDataManualConfigHandler Synonym for std::shared_ptr. */ /*! \brief Constructs the \l{IsotopicDataManualConfigHandler} with \a file_name. */ IsotopicDataManualConfigHandler::IsotopicDataManualConfigHandler( const QString &file_name) : IsotopicDataBaseHandler(file_name) { } /*! \brief Constructs the \l{IsotopicDataManualConfigHandler}. \a isotopic_data_sp Isotopic data \a file_name File name */ IsotopicDataManualConfigHandler::IsotopicDataManualConfigHandler( IsotopicDataSPtr isotopic_data_sp, const QString &file_name) : IsotopicDataBaseHandler(isotopic_data_sp, file_name) { } /*! \brief Destructs the \l{IsotopicDataManualConfigHandler}. */ IsotopicDataManualConfigHandler::~IsotopicDataManualConfigHandler() { // qDebug(); } /*! \brief Assigns \a map to m_symbolCountMap. */ void IsotopicDataManualConfigHandler::setSymbolCountMap(const SymbolCountMap &map) { m_symbolCountMap = map; } /*! \brief Returns a reference to m_symbolCountMap. */ const SymbolCountMap & IsotopicDataManualConfigHandler::getSymbolCountMap() const { return m_symbolCountMap; } /*! \brief Loads isotopic data from \a file_name. Returns the count of \l{Isotope}s that were allocated and stored in the msp_isotopicData member. */ qsizetype IsotopicDataManualConfigHandler::loadData(const QString &file_name) { // File format: // // [Element] // symbol C count 6 // [Isotopes] 2 // mass 12.0 prob 0.989 // mass 13.003354 prob 0.010788 // [Element] // symbol H count 13 // [Isotopes] 2 // mass 1.0078250 prob 0.99988 // mass 2.01410177 prob 0.00011570 // [Element] // symbol O count 6 // [Isotopes] 3 // mass 15.9949 prob 0.99756 // mass 16.999 prob 0.000380 // mass 17.999 prob 0.002051 // QString local_file_name = file_name; if(local_file_name.isEmpty()) local_file_name = m_fileName; // See the Isotope::toString() function that is used to write the isotopic // data to file. We thus expect exactly that format from the file. if(local_file_name.isEmpty()) { qCritical("File name is emtpy. Failed to open file for reading."); return 0; } QFile file(local_file_name); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCritical("Failed to open file for reading."); return 0; } // qDebug() << "Loading isotopic data from file:" << local_file_name; // File-parsing helper variables. bool was_started_one_element = false; bool was_symbol_count_line = false; bool was_isotopes_count_line = false; bool was_mass_prob_line = false; // Instantiate a symbol that we'll use as a place holder for the element name. QString symbol = ""; // Instantiate a count to document the symbol count. qsizetype element_count = 0; // Instantiate a count to document the number of isotopes that were defined // for a given element. qsizetype element_isotope_count = 0; // Increment each time an isotope is parsed and added to the vector *for a // given element stanza*. This helps sanity checking. qsizetype added_isotopes_for_element = 0; // Maintain a counter of the whole count of isotopes for sanity checks. qsizetype all_parsed_isotopes_count = 0; double mass = 0; double prob = 0; bool ok = false; QRegularExpression comment_regexp("^\\s*#"); QRegularExpression symbol_count_regexp( "^\\s*symbol\\s+([A-Z][a-z]?)\\s+count\\s+(\\d+)"); QRegularExpression isotopes_regexp("^\\s*\\[Isotopes\\]\\s(\\d+)"); // QRegularExpression massProbRegexp = QRegularExpression( //"^\\s+mass\\s+(\\d*\\.?\\d*[e]?[-]?[+]?\\d*)\\s+prob\\s+([^\\d^\\.^-]+)(-?" //"\\d*\\.?\\d*[e]?[-]?[+]?\\d*)"); QRegularExpression mass_prob_regexp = QRegularExpression( "^\\s*mass\\s(\\d*\\.?\\d*[e]?[-]?[+]?\\d*)\\sprob\\s(\\d*\\.?\\d*[e]?[" "-]" "?[" "+]?\\d*)"); // qDebug() << "The mass prob regexp is valid?" << // massProbRegexp.isValid(); // mass 13.003354835200 // prob 0.010788058149533083507343178553128382191061973571777343750000 // Make sure we clear the room. msp_isotopicData->clear(); QList element_isotopes; // This set is to ensure that we do not have twice the same element frame // (that is, with the same symbol). std::set symbol_set; // File format: // [Element] // symbol C count 6 // [Isotopes] 2 // mass 12.0 prob 0.989 // mass 13.003354 prob 0.010788 QTextStream in_stream(&file); while(!in_stream.atEnd()) { QString line = in_stream.readLine(); // Ignore empty lines if(line.length() < 1) continue; line = line.simplified(); // qDebug() << "Current line:" << line; // Ignore comment lines QRegularExpressionMatch match = comment_regexp.match(line); if(match.hasMatch()) continue; if(line == "[Element]") { // qDebug() << "That's the [Element] stanza opening line."; // We are starting a new Element stanza. It cannot be that we both // have already started one Element stanza and that not a single // mass and probability line had been encountered. Either this is the // very first Element stanza that we read and was_started_one_element // is false or we were reading one Element stanza that has finished // and then was_mass_prob_line has to be true. if(was_started_one_element && !was_mass_prob_line) { qDebug() << "Error: one element is complete but has no isotopes."; return 0; } if(was_started_one_element) { // qDebug() //<< "We had already seen the [Element] stanza opening line."; // We are starting a new Element configuration stanza, but in // fact another was already cooking. We need to terminate it. // Sanity check: the number of purportedly listed isotopes needs // to be identical to the number of isotopes actually added to the // vector of isotopes. if(element_isotope_count != element_isotopes.size() || added_isotopes_for_element != element_isotopes.size()) { qDebug() << "Error. We did not parse the expected number of " "isotopes."; return 0; } // qDebug() << "And there are the right number of isotopes // cooked."; // At this point we have everything we need to add this new // chemical set. // qDebug() << "Creating new chemical set."; if(!newChemicalSet( symbol, element_count, element_isotopes, false)) { qDebug() << "Failed to add new chemical set."; return 0; } // qDebug() << "The completed chemical set: " //"symbol/element_count/isotope_count:" //<< symbol << "/" << element_count << "/" //<< element_isotopes.size(); // Sanity check: the total count of added isotopes for this symbol // needs to correct. if(added_isotopes_for_element != msp_isotopicData->getIsotopeCountBySymbol(symbol)) qFatal("Programming error."); // Now clear for next run. symbol = ""; element_isotope_count = 0; added_isotopes_for_element = 0; element_isotopes.clear(); } // Tell that we actually have entered the first line of an Element // stanza. was_started_one_element = true; // Reset all the other values so that we know we are just at the // beginning of the parsing work. was_symbol_count_line = false; was_isotopes_count_line = false; was_mass_prob_line = false; // Go the next line. continue; } // At this point we are already inside of the [Element] stanza. Parse the // various lines inside it. // File format: // [Element] // symbol C count 6 // [Isotopes] 2 // mass 12.0 prob 0.989 // mass 13.003354 prob 0.010788 match = symbol_count_regexp.match(line); if(match.hasMatch()) { // qDebug() << "Matched the symbol count line."; // If we are parsing "symbol C count 100", then it is not possible // that we did not encounter before [Element] or that we already have // parsed one same line as "symbol C count 100". if(!was_started_one_element || was_symbol_count_line) { qDebug() << "Error encountered in the symbol/count line."; return 0; } symbol = match.captured(1); element_count = match.captured(2).toInt(&ok); if(!ok || !element_count) { qDebug() << "Error encountered in the symbol/count line."; return 0; } // Now check if that symbol was encountered already, which would be an // error. auto res = symbol_set.insert(symbol); if(!res.second) { // We did not insert the symbol because one already existed. That // is an error. qDebug() << "An element by symbol" << symbol << "has already been processed: " "this is not permitted."; return 0; } // qDebug() << "Processed element symbol:" << symbol //<< "with count:" << element_count; // Do not store the element symbol/count pair yet, we'll wait to // encounter a new Element stanza which will close this one. was_symbol_count_line = true; was_isotopes_count_line = false; was_mass_prob_line = false; continue; } // End of // line matched the symbol/count regexp // File format: // [Element] // symbol C count 6 // [Isotopes] 2 // mass 12.0 prob 0.989 // mass 13.003354 prob 0.010788 match = isotopes_regexp.match(line); if(match.hasMatch()) { // qDebug() << "Matched the [Isotopes] count stanza opening line."; // We cannot be parsing the [Isotopes] stanza opening header if we // have not previously parsed the symbol/count line. if(!was_symbol_count_line) { qDebug() << "Error encounteredd in the isotopes lines."; return 0; } // Store the number of isotopes for the current Element. element_isotope_count = match.captured(1).toInt(&ok); if(!ok || !element_isotope_count) { qDebug() << "Error encounteredd in the isotopes lines."; return 0; } // qDebug() << "The isotope count is:" << element_isotope_count; was_isotopes_count_line = true; was_symbol_count_line = false; was_mass_prob_line = false; continue; } // End of // line matched the [Isotopes] count regexp // File format: // [Element] // symbol C count 6 // [Isotopes] 2 // mass 12.0 prob 0.989 // mass 13.003354 prob 0.010788 match = mass_prob_regexp.match(line); if(match.hasMatch()) { // qDebug() << "Matched the mass prob line."; // If we match an isotope's mass/prob line, either --- at previous // line --- we had seen the [Isotopes] stanza opening line or we had // seen another mass prob line. if(!was_isotopes_count_line && !was_mass_prob_line) { qDebug() << "Error encountered in the mass/prob line."; return 0; } mass = match.captured(1).toDouble(&ok); if(!ok) { qDebug() << "Error encountered in the mass/prob line."; return 0; } prob = match.captured(2).toDouble(&ok); if(!ok) { qDebug() << "Error encountered in the mass/prob line."; return 0; } // At this point we have everything we need to actually create the // isotope by symbol and mass and prob. There are a number of fields // that are left to value 0 but this is of no worries. // qDebug() << "Iterated in isotope:" << symbol << ":" << mass << "/" //<< prob; // At this point create a brand new Isotope with the relevant data. // Isotope::Isotope( // QString element, // QString symbol, // double mass, // double probability) // There are a number of fields that are left to value 0 but this is // of no worries. Store the isotope in the vector of isotopes that // will be added to the isotopic data later when finishing the parsing // of the element frame widget. element_isotopes.push_back( QSharedPointer::create(symbol, symbol, mass, prob)); ++added_isotopes_for_element; ++all_parsed_isotopes_count; // qDebug() << "The atom now is:" << atom.asText(); was_mass_prob_line = true; was_isotopes_count_line = false; was_symbol_count_line = false; continue; } // End of // line matched the isotope mass/prob regexp } // We have finished iterating in the file's lines but we were parsing an atom, // append it. // Sanity check if(!was_started_one_element) { qDebug() << "Error: not a single element could be parsed."; return 0; } // Sanity check: the number of purportedly listed isotopes needs // to be identical to the number of isotopes actually added to the // vector of isotopes. if(element_isotope_count != element_isotopes.size() || added_isotopes_for_element != element_isotopes.size()) { qDebug() << "Error. We did not parse the expected number of " "isotopes."; return 0; } // At this point we have everything we need to add this new // chemical set. Do not yet update the mass maps, we'll do at the end of the // process. if(!newChemicalSet(symbol, element_count, element_isotopes, false)) { qDebug() << "Failed to add new chemical set."; return 0; } // qDebug() << "The completed chemical set: " //"symbol/element_count/isotope_count:" //<< symbol << "/" << element_count << "/" << element_isotopes.size(); // Sanity check: the total count of added isotopes for this symbol // needs to correct. if(added_isotopes_for_element != msp_isotopicData->getIsotopeCountBySymbol(symbol)) qFatal("Programming error."); // Sanity check: the total count of isotopes added to the isotopic data member // datum needs to match the total count of parsed isotopes. if(all_parsed_isotopes_count != msp_isotopicData->size()) qFatal("Programming error."); // We have touched the isotopic data, ensure the maps are current. if(!msp_isotopicData->updateMassMaps()) qFatal("Programming error. Failed to update the mass maps."); m_fileName = local_file_name; return msp_isotopicData->size(); } /*! \brief Write all the IsotopicData to \a file_name. If \a file_name is empty, \l{m_fileName} is tried. If both are empty, the function returns 0. If any one of the file names are correct (\a file_name takes precedence over \l{m_fileName}), then \l{m_fileName} is set to that file name. The format of the file consists in a single line of data per \l{Isotope} as created using Isotope::toString(). Each isotope is output to its own line. Returns the count of \l{Isotope}s written to file or 0 if the file does not exist or is not readable. */ qsizetype IsotopicDataManualConfigHandler::writeData(const QString &file_name) { if(file_name.isEmpty() && m_fileName.isEmpty()) return 0; QString temp_file_name; // The passed filename takes precedence over the member datum. So copy // that file name to the member datum. if(!file_name.isEmpty()) temp_file_name = file_name; else temp_file_name = m_fileName; QFile file(temp_file_name); if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qDebug("Failed to open file for writing."); return false; } QTextStream out(&file); out << "# This file contains isotopic data in a format that can accommodate\n"; out << "# comments in the form of lines beginning with the '#' character.\n"; qsizetype isotope_count = 0; // We want to write the isotopic data exactly in the same order as we might // have loaded them. This is why we need to iterated in the vector of isotopes // and not the in the map that is ordered according to the symbols (the keys). QString last_symbol; for(auto item : msp_isotopicData->m_isotopes) { QString symbol = item->getSymbol(); // We only write the  [Element] and [Isotopes] stanza header once for each // new symbol found in the iterated items. if(symbol != last_symbol) { // This is the first time we encounter an isotope by symbol, we open a // new Element stanza. out << "[Element]\n"; // We now need to write the symbol count line. If the symbol key is // not found, throws an exception. int symbol_count = m_symbolCountMap.at(symbol); out << QString("\tsymbol %1 count %2\n").arg(symbol).arg(symbol_count); // We also need to write the Isotopes stanza: int symbol_isotope_count = msp_isotopicData->getIsotopeCountBySymbol(symbol); out << QString("\t[Isotopes] %1\n").arg(symbol_isotope_count); } // At this point we can write the currently iterated isotope's mass:prob // pair. out << QString("\t\tmass %1 prob %2\n") .arg(item->getMass(), 0, 'f', 60) .arg(item->getProbability(), 0, 'f', 60); last_symbol = symbol; ++isotope_count; } out.flush(); file.close(); if(isotope_count != msp_isotopicData->size()) qFatal("Programming error. Failed to write all the isotopes to file."); // Now we know that temp_file_name is fine. Store into m_fileName. m_fileName = temp_file_name; return isotope_count; } /*! \brief Add a set of \l{Isotope}s belonging to chemical element \a symbol. The count of atoms of \a symbol is defined with \a element_count. The isotopes to be added are provided in \a isotopes. If the new chemical data pertain to a symbol that was already added to the IsotopicData, then that is an error. If \a update_maps is true, the \l{IsotopicData} maps need to be updated. Returns false if the new chemical data pertain to a \a symbol that was already added to the IsotopicData. \sa IsotopicData::updateMonoMassMap \sa IsotopicData::updateAvgMassMap */ bool IsotopicDataManualConfigHandler::newChemicalSet( const QString &symbol, int element_count, const QList &isotopes, bool update_maps) { // It is of uttermost importance that we update the symbol/count pair because // that is going to be used when performing the IsoSpec arrays configuration // and later isotopic cluster calculations. std::pair res = m_symbolCountMap.insert(std::pair(symbol, element_count)); if(!res.second) { // Sanity check: it is not possible to add new chemical sets by a given // symbol multiple times. qDebug() << "Error: isotopes by that symbol: " << symbol << "were already found in the isotopic data set."; return false; } qsizetype count_before = msp_isotopicData->size(); // Update the mass maps according to second param below. msp_isotopicData->appendNewIsotopes(isotopes, update_maps); qsizetype count_after = msp_isotopicData->size(); if(count_after - count_before != isotopes.size()) qFatal("Programming error."); return true; } /*! \brief Returns a string containing a formula corresponding to the IsotopicData. The formula is actually computed using the symbol/count pairs stored in m_symbolCountMap. */ QString IsotopicDataManualConfigHandler::craftFormula() const { QString formula; for(auto item : m_symbolCountMap) formula.append(QString("%1%2").arg(item.first).arg(item.second)); return formula; } /*! \brief Returns the count of \l{Isotope}s in this collection. */ qsizetype IsotopicDataManualConfigHandler::checkConsistency() { return msp_isotopicData->size(); } } // namespace libXpertMassCore } // namespace MsXpS #if 0 Example from IsoSpec. const int elementNumber = 2; const int isotopeNumbers[2] = {2,3}; const int atomCounts[2] = {2,1}; const double hydrogen_masses[2] = {1.00782503207, 2.0141017778}; const double oxygen_masses[3] = {15.99491461956, 16.99913170, 17.9991610}; const double* isotope_masses[2] = {hydrogen_masses, oxygen_masses}; const double hydrogen_probs[2] = {0.5, 0.5}; const double oxygen_probs[3] = {0.5, 0.3, 0.2}; const double* probs[2] = {hydrogen_probs, oxygen_probs}; IsoLayeredGenerator iso(Iso(elementNumber, isotopeNumbers, atomCounts, isotope_masses, probs), 0.99); #endif libxpertmass-1.4.0/source/XpertMassCore/src/IsotopicDataUserConfigHandler.cpp000664 001750 001750 00000017475 15100504560 030653 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include /////////////////////// IsoSpec #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::IsotopicDataUserConfigHandler \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile IsotopicDataUserConfigHandler.hpp \brief The IsotopicDataUserConfigHandler class handles user-defined \l{IsotopicData}. The data can be loaded and written to file. The format of the data is based on Isotope::toString(), that outputs each \l{Isotope} data in a separate line. Comments are allowed and are on lines that start with the '#' character. These lines are ignored when loading data. \sa IsotopicDataLibraryHandler, IsotopicDataManualConfigHandler \sa Isotope::toString */ /*! \brief Constructs the \l{IsotopicDataUserConfigHandler}. m_fileName is set to \a file_name */ IsotopicDataUserConfigHandler::IsotopicDataUserConfigHandler( const QString &file_name) : IsotopicDataBaseHandler(file_name) { } /*! \brief Constructs the IsotopicDataUserConfigHandler. The instance will have its isotopic data member pointing to \a isotopic_data_sp. m_fileName is set to \a file_name */ IsotopicDataUserConfigHandler::IsotopicDataUserConfigHandler( IsotopicDataSPtr isotopic_data_sp, const QString &file_name) : IsotopicDataBaseHandler(isotopic_data_sp, file_name) { } /*! \brief Destructs the IsotopicDataUserConfigHandler. Nothing is explicitely deleted in the destructor. */ IsotopicDataUserConfigHandler::~IsotopicDataUserConfigHandler() { // qDebug(); } /*! \brief Loads isotopic data from \a file_name if set. If \a file_name is empty, then the member m_fileName is used. The format of the file consists in a single line of data per \l{Isotope} as created using the Isotope::toString() function. Each line is used to create an Isotope with the text-based constructor. Returns the count of \l{Isotope}s created or 0 if the file does not exist or is not readable. \sa Isotope::Isotope and overloads */ qsizetype IsotopicDataUserConfigHandler::loadData(const QString &file_name) { QString local_file_name = file_name; if(local_file_name.isEmpty()) local_file_name = m_fileName; // See the Isotope::toString() function that is used to write the isotopic // data to file. We thus expect exactly that format from the file. if(local_file_name.isEmpty()) { qCritical("File name is emtpy. Failed to open file for reading."); return 0; } QFile file(local_file_name); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCritical("Failed to open file for reading."); return 0; } // qDebug() << "Loading isotopic data from file:" << local_file_name; // We may have written comments to the file in the form of '# ' lines QRegularExpression commentRegexp("^\\s*#.*$"); msp_isotopicData->clear(); qsizetype isotope_count = 0; QTextStream in_stream(&file); while(!in_stream.atEnd()) { QString line = in_stream.readLine().simplified(); // qDebug() << "simplified line:" << line; // Ignore empty or comment lines if(line.length() < 1 || commentRegexp.match(line).hasMatch()) continue; IsotopeQSPtr isotope_qsp = QSharedPointer::create(line); // We do not want to update the mono/avg maps each time we load an // isotope. We'll call the relevant function later. msp_isotopicData->appendNewIsotope(isotope_qsp, /* update_maps*/ false); ++isotope_count; } // End of // while(!in.atEnd()) // qDebug() << "Finished creating all the Isotope instances."; file.close(); // At this point, it seems that the loading went fine. // Because we have touched the m_isotopes vector, we need to update the // mono/avg masses map. if(!msp_isotopicData->updateMassMaps()) qFatal("Programming error. Failed to update the mass maps."); if(isotope_count != msp_isotopicData->size()) qFatal("Programming error. Failed to load all the isotopes to file."); return msp_isotopicData->size(); } /*! \brief Writes isotopic data to \a file_name. If \a file_name is empty, \l{m_fileName} is tried. If both are empty, the function returns 0. If any one of the file names are correct (file_name takes precedence over m_fileName), then m_fileName is set to that file name. The format of the file consists in a single line of data per \l Isotope as created using the Isotope::toString() function. Each isotope is output to its own line. Returns the count of \l{Isotope}s written to file or 0 if the file does not exist or is not readable. */ qsizetype IsotopicDataUserConfigHandler::writeData(const QString &file_name) { // Although the isotopic data were loaded from the IsoSpec library tables, we // might be willing to store these data to a file. QString local_file_name = file_name; if(local_file_name.isEmpty()) local_file_name = m_fileName; if(local_file_name.isEmpty()) { qCritical("File name is emtpy. Failed to open file for writing."); return 0; } QFile file(local_file_name); if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCritical("Failed to open file for writing."); return 0; } QTextStream out_stream(&file); out_stream << "# This file contains isotopic data in a format that can accommodate\n"; out_stream << "# comments in the form of lines beginning with the '#' character.\n\n"; qsizetype isotope_count = 0; for(auto item : msp_isotopicData->m_isotopes) { out_stream << item->toString(); // We need to add it because toString() does not terminate the line with // a new line character. out_stream << "\n"; ++isotope_count; } out_stream.flush(); file.close(); if(isotope_count != msp_isotopicData->size()) qFatal("Programming error. Failed to write all the isotopes to file."); // Now we know that local_file_name is fine. Store into m_fileName. m_fileName = local_file_name; return isotope_count; } /*! \brief Returns the size of the member IsotopicData. */ qsizetype IsotopicDataUserConfigHandler::checkConsistency() { return msp_isotopicData->size(); } } // namespace libXpertMassCore } // namespace MsXpS #if 0 Example from IsoSpec. const int elementNumber = 2; const int isotopeNumbers[2] = {2,3}; const int atomCounts[2] = {2,1}; const double hydrogen_masses[2] = {1.00782503207, 2.0141017778}; const double oxygen_masses[3] = {15.99491461956, 16.99913170, 17.9991610}; const double* isotope_masses[2] = {hydrogen_masses, oxygen_masses}; const double hydrogen_probs[2] = {0.5, 0.5}; const double oxygen_probs[3] = {0.5, 0.3, 0.2}; const double* probs[2] = {hydrogen_probs, oxygen_probs}; IsoLayeredGenerator iso(Iso(elementNumber, isotopeNumbers, atomCounts, isotope_masses, probs), 0.99); #endif libxpertmass-1.4.0/source/XpertMassCore/src/MassCollection.cpp000664 001750 001750 00000022762 15100504560 025717 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/MassCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassCollection \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MassCollection.hpp \brief The MassCollection class provides a container for masses (as double values) and for text representing thoses values. The MassCollection class provides a container for masses (as double values) and for text representing thoses values. Methods allow to create numerical mass values starting for a text string representing mass values separated by newline characters and, reciprocally, create a text string starting from all the mass values. The mass values are stored in a container (m_masses) and the text string is stored in m_massText. */ /*! \variable MsXpS::libXpertMassCore::MassCollection::m_name \brief The name associated to the MassCollection. */ /*! \variable MsXpS::libXpertMassCore::MassCollection::m_comment \brief The comment associated to the MassCollection. */ /*! \variable MsXpS::libXpertMassCore::MassCollection::m_masses \brief The container of mass double values. */ /*! \brief Constructs a MassCollection with \a name. The instance is created in an invalid state. */ MassCollection::MassCollection(const QString &name) { m_name = name; } /*! \brief Constructs a MassCollection with \a name and \a mass_text. The \a mass_text is the textual representation of the mass double values that is parsed and converted to mass double values. After the parsing, this instance is validated, and the result is set to m_isValid. */ MassCollection::MassCollection(const QString &name, const QString &mass_text) { m_name = name; ErrorList error_list; if(textToMasses(mass_text, &error_list) == -1) qCritical() << "Constructing a MassCollection with text not suitable for " "representing mass double values."; // Will set m_isValid to the right value. if(!validate(&error_list)) qWarning() << "Constructing a MassCollection that does not validate " "successfully, with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Constructs a MassCollection with \a name and \a masses. The masses are copied to the member container without check. If the masses container is non-empty, the m_isValid status is set to true, else to false. */ MassCollection::MassCollection(const QString &name, const std::vector &masses) { m_name = name; m_masses = masses; m_isValid = m_masses.size(); } /*! \brief Destructs this MassCollection. */ MassCollection::~MassCollection() { } /*! \brief Constructs a MassCollection as a copy of \a other. If the masses container is non-empty, the m_isValid status is set to true, else to false. */ MassCollection::MassCollection(const MassCollection &other) : m_name(other.m_name), m_comment(other.m_comment), m_masses(other.m_masses) { m_isValid = m_masses.size(); } /*! \brief Assigns \a other to this MassCollection and returns a reference to this object. If the masses are non-empty, the m_isValid status is set to true, else to false. */ MassCollection & MassCollection::operator=(const MassCollection &other) { if(&other == this) return *this; m_name = other.m_name; m_comment = other.m_comment; m_masses = other.m_masses; m_isValid = m_masses.size(); return *this; } /*! \brief Sets the name to \a name. */ void MassCollection::setName(const QString &name) { m_name = name; } /*! \brief Returns the name. */ const QString & MassCollection::getName() const { return m_name; } /*! \brief Sets the comment to \a comment. */ void MassCollection::setComment(const QString &comment) { m_comment = comment; } /*! \brief Returns the comment. */ const QString & MassCollection::getComment() const { return m_comment; } /*! \brief Returns the size of the container of masses. */ std::size_t MassCollection::size() const { return m_masses.size(); } /*! \brief Returns a const reference to the container of masses. */ const std::vector & MassCollection::getMassesCstRef() const { return m_masses; } /*! \brief Returns a reference to the container of masses. */ std::vector & MassCollection::getMassesRef() { return m_masses; } /*! \brief Returns the mass at the \a index in the container. An out of bounds index is fatal. */ double MassCollection::getMassAtIndex(std::size_t index) const { if(index >= m_masses.size()) qFatalStream() << "Programming error. The index is out of bounds."; return m_masses.at(index); } /*! \brief Returns the count of masses successfully obtained by converting the \a text to double values. The text is first split at newline characters and each obtained string is converted to a double value that is added to the container. The mass container is first emptied and the validity status of the MassCollection is set to false. If the \a text is empty, this function returns 0. If a conversion error is encountered, the faulty text element is appended to \a error_list_p, the mass container is cleared, m_isValid is set to false and -1 is returned. If the container of masses is non-empty at the end of the conversion (without error), then m_isValid is set to true and the container size is returned. */ int MassCollection::textToMasses(const QString &text, ErrorList *error_list_p) { // qDebug() << "Now converting textual masses to numerical masses:" << text; qsizetype error_count = error_list_p->size(); m_masses.clear(); m_isValid = false; if(text.isEmpty()) return 0; QStringList lines = text.split("\n", Qt::SkipEmptyParts); QString line; foreach(line, lines) { bool ok; double value = line.toDouble(&ok); if(!ok) { qCritical() << "This text element failed to convert to double:" << line; error_list_p->push_back(line); break; } m_masses.push_back(value); } if(error_list_p->size() - error_count) { m_masses.clear(); return -1; } if(m_masses.size()) m_isValid = true; qDebug() << "Converted to double " << m_masses.size() << "masses"; return m_masses.size(); } /*! \brief Returns a text string representing the mass double values in the container all separated with a newline character. */ QString MassCollection::massesToText() { QString text; for(double mass : m_masses) text += QString("%1\n").arg(mass, 0, 'f', OLIGOMER_DEC_PLACES); return text; } /*! \brief Sorts the masses in the container in ascending order. */ void MassCollection::sortAscending() { std::sort(m_masses.begin(), m_masses.end()); } /*! \brief Sorts the masses in the container in descending order. */ void MassCollection::sortDescending() { std::sort(m_masses.begin(), m_masses.end(), std::greater()); } /*! \brief Adds \a mass to each mass in the container. */ void MassCollection::addMass(double mass) { for(double &iter_mass : m_masses) iter_mass += mass; } /*! \brief Removes from the container all mass values strictly below \a threshold. */ void MassCollection::removeLessThan(double threshold) { std::vector::iterator the_iterator = m_masses.begin(); std::vector::iterator the_end_iterator = m_masses.end(); while(the_iterator != the_end_iterator) { if((*the_iterator) < threshold) the_iterator = m_masses.erase(the_iterator); else ++the_iterator; } } /*! \brief Validates this MassCollection and returns true if successful or false otherwise. Upon validation, error messages are set to \a error_list_p (not emptied). If validation is successful, the m_isValid validity status is set to true, to false otherwise. */ bool MassCollection::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(!m_masses.size()) error_list_p->push_back( "This MassCollection does not validate successfully: there are no " "masses."); m_isValid = error_count != error_list_p->size() ? false : true; return m_isValid; } /*! \brief Returns the validity status of this MassCollection. */ bool MassCollection::isValid() const { return m_isValid; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassDataCborBaseHandler.cpp000664 001750 001750 00000022221 15100504560 027362 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes /////////////////////// Qt includes #include #include /////////////////////// IsoSpec /////////////////////// Local includes #include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassDataCborBaseHandler \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MassDataCborBaseHandler.hpp \brief The MassDataCborBaseHandler class provides features to handle mass spectrometric data using the CBOR (Concise Binary Object Representation) container streaming classes. The data transported using CBOR is first qualified using the \l{Enums::MassDataType} enum. The data are packed following that mass data type specification. */ /*! \brief Constructs a MassDataCborBaseHandler instance setting parent to \a parent_p. */ MassDataCborBaseHandler::MassDataCborBaseHandler(QObject *parent_p) : QObject(parent_p) { } /*! \brief Destructs the MassDataCborBaseHandler instance. */ MassDataCborBaseHandler::~MassDataCborBaseHandler() { } /*! \brief Sets the input file name to \a file_name. */ void MassDataCborBaseHandler::setInputFileName(const QString &file_name) { m_inputFileName = file_name; } /*! \brief Sets the output file name to \a file_name. */ void MassDataCborBaseHandler::setOutputFileName(const QString &file_name) { m_outputFileName = file_name; } /*! \brief Sets the mass data type to \a mass_data_type. */ void MassDataCborBaseHandler::setMassDataType(Enums::MassDataType mass_data_type) { m_massDataType = mass_data_type; } /*! \brief Returns the mass data type. */ Enums::MassDataType MassDataCborBaseHandler::getMassDataType() const { return m_massDataType; } /*! \brief Decodes the data stream from \a reader_sp. \note The base class does nothing and always returns false. */ bool MassDataCborBaseHandler::decodeStream( [[maybe_unused]] QCborStreamReaderSPtr &reader_sp) { qDebug() << "The base class handler function does nothing."; return false; } /*! \brief Decodes enough of the data streamed by \a reader_sp to determine and return the mass data type of the streamed data. \note Failure to decode the mass data type is fatal. */ Enums::MassDataType MassDataCborBaseHandler::readMassDataType(QCborStreamReaderSPtr &reader_sp) { QString current_key; // The file format starts with a quint64 that indicates the type of mass // data (MassDataType enum to quint64). while(!reader_sp->lastError() && reader_sp->hasNext()) { // The first iteration in the CBOR data structure is in a map. QCborStreamReader::Type type = reader_sp->type(); // qDebug() << "Type is:" << type; if(type != QCborStreamReader::Map) qFatal( "The byte array does not have the expected CBOR structure for " "mass data."); reader_sp->enterContainer(); while(reader_sp->lastError() == QCborError::NoError && reader_sp->hasNext()) { QCborStreamReader::Type type = reader_sp->type(); // qDebug() << "Type is:" << type; // The very first item of the map should be a pair // // "DATA_TYPE / quint64 if(type == QCborStreamReader::String) { // Read a CBOR string, concatenating all // the chunks into a single string. QString str; auto chunk = reader_sp->readString(); while(chunk.status == QCborStreamReader::Ok) { str += chunk.data; chunk = reader_sp->readString(); } if(chunk.status == QCborStreamReader::Error) { // handle error condition qDebug() << "There was an error reading string chunk."; str.clear(); } // qDebug() << "The string that was read:" << str; // If the current key is empty, this string value is a new key // or tag. Otherwise, it's a value. if(current_key.isEmpty()) { // qDebug() << "Setting current_key:" << str; current_key = str; } else { // At this point we can reset current_key. current_key = QString(); } } else if(type == QCborStreamReader::UnsignedInteger) { if(current_key != "DATA_TYPE") qFatal( "The byte array does not have the expected CBOR " "structure for " "mass data."); // quint64 data_type = reader_sp->toUnsignedInteger(); // qDebug() << "The mass data type:" << data_type; return static_cast( reader_sp->toUnsignedInteger()); } else qFatal( "The byte array does not have the expected CBOR " "structure for mass data."); } } return Enums::MassDataType::NOT_SET; } /*! \overload \brief Decodes enough of the data contained in file \a input_file_name to determine and return the mass data type of the streamed data. \note Failure to decode the mass data type is fatal. \sa readMassDataType() overloads */ Enums::MassDataType MassDataCborBaseHandler::readMassDataType(const QString &input_file_name) { QFileInfo file_info(input_file_name); if(!file_info.exists()) { qDebug() << "File not found."; return Enums::MassDataType::NOT_SET; } QFile file(input_file_name); bool res = file.open(QIODevice::ReadOnly); if(!res) { qDebug() << "Failed to open the file for read."; return Enums::MassDataType::NOT_SET; } QCborStreamReaderSPtr reader_sp = std::make_shared(&file); return readMassDataType(reader_sp); } /*! \overload \brief Decodes enough of the data contained in \a byte_array to determine and return the mass data type of the streamed data. \note Failure to decode the mass data type is fatal. \sa readMassDataType() overloads */ Enums::MassDataType MassDataCborBaseHandler::readMassDataType(const QByteArray &byte_array) { QCborStreamReaderSPtr reader_sp = std::make_shared(byte_array); return readMassDataType(reader_sp); } /*! \brief Reads the data in file \a input_file_name by decoding the data stream read from it. \note The base class does nothing and always returns false. */ bool MassDataCborBaseHandler::readFile( [[maybe_unused]] const QString &input_file_name) { qDebug() << "The base class handler function does nothing."; return false; } /*! \brief Reads the data in byte array \a byte_array by decoding the data stream read from it. \note The base class does nothing and always returns false. */ bool MassDataCborBaseHandler::readByteArray( [[maybe_unused]] const QByteArray &byte_array) { qDebug() << "The base class handler function does nothing."; return false; } /*! \brief Writes member data to file \a output_file_name. \note The base class does nothing and always returns false. */ bool MassDataCborBaseHandler::writeFile( [[maybe_unused]] const QString &output_file_name) { qDebug() << "The base class handler function does nothing."; return false; } /*! \brief Writes member data to byte array \a byte_array. \note The base class does nothing and always returns false. */ void MassDataCborBaseHandler::writeByteArray([[maybe_unused]] QByteArray &byte_array) { qDebug() << "The base class handler function does nothing."; } /*! \brief Sets the \a title of the mass data entity carried by the CBOR container. */ void MassDataCborBaseHandler::setTitle(const QString &title) { m_title = title; } /*! \brief Returns the title of the mass data entity carried by the CBOR container. */ QString MassDataCborBaseHandler::getTitle() const { return m_title; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassDataCborMassSpectrumHandler.cpp000664 001750 001750 00000046767 15100504560 031163 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes /////////////////////// Qt includes #include #include /////////////////////// IsoSpec /////////////////////// Local includes #include "MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassDataCborMassSpectrumHandler \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MassDataCborMassSpectrumHandler.hpp \brief The MassDataCborMassSpectrumHandler class provides features to handle mass spectrum data using the CBOR (Concise Binary Object Representation) container streaming classes. The data transported using CBOR is first qualified using the \l{Enums::MassDataType::MASS_SPECTRUM} value. The data are packed following that mass data type specification. The format for this kind of CBOR mass spectrum data is the following: The whole file is actually a map that contains the key/value pairs below. All the text string values below are described in this way in the CBOR Qt implementation specification: "Major type 3: a text string, specifically a string of Unicode characters that is encoded as UTF-8 (that is, not a QByteArray, but a QString)". The containers, map, strings and byte arrays dot not need the reader's next() call. The simple value, like integers, do need the reader's next() call. In this format, the whole data set in the CBOR container is a map with the following key/value pairs: \e{start_map} \list \li "DATA_TYPE" / value (quint64) : this quint64 (QCborStreamReader::UnsignedInteger) value is only got as the very first data bit in the file and represents the Enums::MassDataType::MASS_SPECTRUM value describing the kind of data encoded in the CBOR container. \li "TITLE" / value (text string): the title of the mass spectrum that should be used to identify it later in a mass data visualization/exploration program like MineXpert. \li "TRACE_COLOR" / value (QByteArray) \li "X_LABEL" / value (text string): the label of the x abscissae axis. \li "X_DATA" / value (base64 QByteArray): the array of Base64-encoded m/z values (x axis) \li "Y_LABEL" / value (text string): the label of the y ordinates axis. \li "Y_DATA" / value (base64 QByteArray): the array of Base64-encoded intensity values (y axis) \endlist \e{end_map} All the data above fully qualify a mass spectrum in order to display it as a trace (that is, a graph) in a mass spectrometric data visualization/exploration program like MineXpert, from \l{http://www.msxpertsuite.org/}. */ /*! \brief Constructs a MassDataCborMassSpectrumHandler instance setting parent to \a parent_p. */ MassDataCborMassSpectrumHandler::MassDataCborMassSpectrumHandler( QObject *parent_p) : MassDataCborBaseHandler(parent_p) { qDebug() << "Current thread:" << QThread::currentThread() << "is main thread:" << QThread::isMainThread(); } /*! \brief Constructs a MassDataCborMassSpectrumHandler instance initializing the member trace to \a trace and setting parent to \a parent_p. \note The \a trace is considered the mass spectrum data to be streamed to a file as a CBOR container. */ MassDataCborMassSpectrumHandler::MassDataCborMassSpectrumHandler( QObject *parent_p, const pappso::Trace &trace) : MassDataCborBaseHandler(parent_p), m_trace(trace) { } /*! \brief Destructs the MassDataCborMassSpectrumHandler instance. */ MassDataCborMassSpectrumHandler::~MassDataCborMassSpectrumHandler() { } /*! \brief Decodes the data stream from \a reader_sp. The CBOR container consists in a succession of map key/value pairs. The very first key/value pair is "DATA_TYPE"/Enums::MassDataType::MASS_SPECTRUM. The following key/value pairs contain data like the title of the mass spectrum, the color for tracing the mass spectrum, the x (m/z) data and the y (intensity) data. See the class documentation for details. Data decoded from the \a reader_sp stream are set to member data for later consumption. Returns true if the decoding was successful, false otherwise. \sa writeFile() */ bool MassDataCborMassSpectrumHandler::decodeStream(QCborStreamReaderSPtr &reader_sp) { // See QDoc class description above while(reader_sp->lastError() == QCborError::NoError && reader_sp->hasNext()) { QCborStreamReader::Type type = reader_sp->type(); // qDebug() << "Type is:" << type; if(type == QCborStreamReader::UnsignedInteger) { // In this format, the QCborStreamReader::UnsignedInteger datum is // only got as the very first data bit in the file. This bit of // information is a quint64 representing // Enums::MassDataType::MASS_SPECTRUM. // Now check that the read value actually corresponds to the expected // mass data type for which this object is working! // The m_currentKey value was set in a previous loop iteration below // where the type of data read from the stream was the string key // "DATA_TYPE" and we now, for this loop iteratation get the value // for that key. if(m_currentKey == "DATA_TYPE") { // quint64 data_type = 0; // data_type = reader_sp->toUnsignedInteger(); // qDebug() << "The mass data type:" << data_type; if(static_cast( reader_sp->toUnsignedInteger()) != Enums::MassDataType::MASS_SPECTRUM) { qDebug() << "The expected DATA_TYPE::MASS_SPECTRUM was not found."; return false; } m_massDataType = Enums::MassDataType::MASS_SPECTRUM; m_currentKey = QString(); } // qDebug() << "At this point, check if it has next:" //<< reader_sp->hasNext(); // We had what we wanted, go to next. reader_sp->next(); } // End of // if(type == QCborStreamReader::UnsignedInteger) else if(type == QCborStreamReader::String) { // Read a CBOR string, concatenating all // the chunks into a single string. QString str; auto chunk = reader_sp->readString(); while(chunk.status == QCborStreamReader::Ok) { str += chunk.data; chunk = reader_sp->readString(); } if(chunk.status == QCborStreamReader::Error) { // handle error condition qDebug() << "There was an error reading string chunk."; str.clear(); } // qDebug() << "The string that was read:" << str; // If the current key is empty, this string value is a new key or tag. // Otherwise, it's a value. if(m_currentKey.isEmpty()) { // qDebug() << "Setting m_currentKey:" << str; m_currentKey = str; } else { // Depending on what we had already, we will set the proper member // datum. if(m_currentKey == "TITLE") { // qDebug() << "Setting m_title:" << str; m_title = str; } if(m_currentKey == "X_LABEL") { // qDebug() << "Setting m_xLabel:" << str; m_xLabel = str; } if(m_currentKey == "Y_LABEL") { // qDebug() << "Setting m_yLabel:" << str; m_yLabel = str; } // At this point we can reset m_currentKey. m_currentKey = QString(); } } // The ByteArray data for X_DATA and Y_DATA. // They are preceded by the keys "X_DATA" and "Y_DATA" respectively. else if(type == QCborStreamReader::ByteArray) { // Read a byte array. That could be either the X_DATA or the Y_DATA. QByteArray array; auto chunk = reader_sp->readByteArray(); while(chunk.status == QCborStreamReader::Ok) { array.append(chunk.data); chunk = reader_sp->readByteArray(); } if(m_currentKey == "X_DATA") { m_xBase64Data = array; } else if(m_currentKey == "Y_DATA") { m_yBase64Data = array; } else if(m_currentKey == "TRACE_COLOR") { m_colorByteArray = array; } else { qDebug() << "Error in the data read from file."; return false; } m_currentKey = QString(); } else if(type == QCborStreamReader::Map) { reader_sp->enterContainer(); // Read elements until end of map is reached bool res = decodeStream(reader_sp); if(res) reader_sp->leaveContainer(); else return false; } else { // Ignore all other types, go to the next element reader_sp->next(); } } if(reader_sp->lastError() != QCborError::NoError) qDebug() << "There was an error: " << reader_sp->lastError() << "Returning false."; // Return true if there were no errors // qDebug() << "Returning: " << !reader_sp->lastError(); return !reader_sp->lastError(); } /*! \brief Writes member data to file \a output_file_name. \note The member data are packed in a CBOR container and streamed to the file as a set of key/value pairs that form the CBOR map being streamed to the file. Returns true if the operation was successful, false, otherwise. */ bool MassDataCborMassSpectrumHandler::writeFile(const QString &output_file_name) { QString local_file_name = output_file_name; if(local_file_name.isEmpty()) local_file_name = m_outputFileName; QFile file(local_file_name); bool res = file.open(QIODevice::WriteOnly); if(!res) { qDebug() << "Failed to open the file for write."; return false; } msp_writer = std::make_shared(&file); // qDebug() << "Now writing data to file:" << local_file_name; // Start of array containing all mapped items msp_writer->startMap(7); // First the Enums::MassDataType: this is the very *first* data bit in the // data stream. msp_writer->append("DATA_TYPE"); msp_writer->append(static_cast(Enums::MassDataType::MASS_SPECTRUM)); msp_writer->append("TITLE"); msp_writer->append(m_title); msp_writer->append("TRACE_COLOR"); msp_writer->append(m_colorByteArray); msp_writer->append("X_LABEL"); msp_writer->append("m/z"); msp_writer->append("X_DATA"); msp_writer->append(m_trace.xAsBase64Encoded()); msp_writer->append("Y_LABEL"); msp_writer->append("intensity"); msp_writer->append("Y_DATA"); msp_writer->append(m_trace.yAsBase64Encoded()); msp_writer->endMap(); // Close the map now. file.close(); return res; } /*! \brief Writes member data to byte array \a byte_array. \note The member data are packed in a CBOR container and streamed to the byte array as a set of key/value pairs that form the CBOR map being streamed to the byte array. */ void MassDataCborMassSpectrumHandler::writeByteArray(QByteArray &byte_array) { msp_writer = std::make_shared(&byte_array); // qDebug() << "Now writing data byte array."; // Start of array containing all mapped items msp_writer->startMap(7); // First the Enums::MassDataType: this is the very *first* data bit in the // data stream. msp_writer->append("DATA_TYPE"); msp_writer->append(static_cast(Enums::MassDataType::MASS_SPECTRUM)); msp_writer->append("TITLE"); msp_writer->append(m_title); msp_writer->append("TRACE_COLOR"); msp_writer->append(m_colorByteArray); msp_writer->append("X_LABEL"); msp_writer->append("m/z"); msp_writer->append("X_DATA"); msp_writer->append(m_trace.xAsBase64Encoded()); msp_writer->append("Y_LABEL"); msp_writer->append("intensity"); msp_writer->append("Y_DATA"); msp_writer->append(m_trace.yAsBase64Encoded()); msp_writer->endMap(); // Close the map now. } /*! \brief Reads data from the byte array \a byte_array. The data in \a byte_array are packed in a CBOR container. These data have typically travelled over the network and the receiving end needs to unpack the data in that \a byte_array. This is what this function does. The data unpacked from \a byte_array are stored in this object member variables for later consumption by the user of this function. A CBOR stream reader is allocated and set to decode \a byte_array. The actual decoding occurs in \l{decodeStream()}. Returns true if the operations was successful, false otherwise. */ bool MassDataCborMassSpectrumHandler::readByteArray(const QByteArray &byte_array) { msp_reader = std::make_shared(byte_array); bool res = decodeStream(msp_reader); // qDebug() << "After finishing the read, m_title is:" << m_title //<< "and mass data type is:" << static_cast(m_massDataType); // At this point we need to decode the arrays and with the data initialize the // pappso::Trace. QByteArray x_array; QByteArray y_array; QByteArray::FromBase64Result decoding_result = QByteArray::fromBase64Encoding( m_xBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals); if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok) x_array = decoding_result.decoded; else { qDebug() << "Failed to decode the " << m_xLabel << "data"; return false; } decoding_result = QByteArray::fromBase64Encoding( m_yBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals); if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok) y_array = decoding_result.decoded; else { qDebug() << "Failed to decode the " << m_yLabel << "data"; return false; } m_trace.clear(); m_trace.initialize(QString(x_array), QString(y_array)); return res; } /*! \brief Reads the data in file \a input_file_name by decoding the data stream read from it. This function first decodes the data in the CBOR container stored in the file. Then it decodes the Base64-encoded x and y axis data into the member pappso::Trace object. Returns true if the operation was successful, false, otherwise. \sa decodeStream(), readByteArray() */ bool MassDataCborMassSpectrumHandler::readFile(const QString &input_file_name) { // The format for this kind of CBOR pappso::Trace data is the following: // First the quint64 that represents Enums::MassDataType. In our specific // case, that must be Enums::MassDataType::MASS_SPECTRUM. // Then there is the title of data, which is according to the specification: // // Major type 3: a text string, specifically a string of Unicode characters // that is encoded as UTF-8 (that is, not a QByteArray, but a QString). // Then there is a MAP with the following key/value pairs: // start_map // "X_LABEL" / value (text string) // "Y_LABEL" / value (text string) // // "X_DATA" / value (base64 ByteArray) // "Y_DATA" / value (base64 ByteArray) // end_map QString local_file_name = input_file_name; if(local_file_name.isEmpty()) local_file_name = m_inputFileName; QFileInfo file_info(local_file_name); if(!file_info.exists()) { qDebug() << "File not found."; return false; } QFile file(local_file_name); bool res = file.open(QIODevice::ReadOnly); if(!res) { qDebug() << "Failed to open the file for read."; return false; } // qDebug() << "Now starting the CBOR data read."; msp_reader = std::make_shared(&file); res = decodeStream(msp_reader); file.close(); // qDebug() << "After finishing the read, m_title is:" << m_title //<< "and mass data type is:" << static_cast(m_massDataType); // At this point we need to decode the arrays and with the data initialize the // pappso::Trace. QByteArray x_array; QByteArray y_array; QByteArray::FromBase64Result decoding_result = QByteArray::fromBase64Encoding( m_xBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals); if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok) x_array = decoding_result.decoded; else { qDebug() << "Failed to decode the " << m_xLabel << "data"; return false; } decoding_result = QByteArray::fromBase64Encoding( m_yBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals); if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok) y_array = decoding_result.decoded; else { qDebug() << "Failed to decode the " << m_yLabel << "data"; return false; } m_trace.clear(); m_trace.initialize(QString(x_array), QString(y_array)); return res; } /*! \brief Sets the abscissae \a label for the mass spectrum data. */ void MassDataCborMassSpectrumHandler::setXLabel(const QString &label) { m_xLabel = label; } /*! \brief Returns the abscissae label for the mass spectrum data. */ QString MassDataCborMassSpectrumHandler::getXLabel() const { return m_xLabel; } /*! \brief Sets the ordinates \a label for the mass spectrum data. */ void MassDataCborMassSpectrumHandler::setYLabel(const QString &label) { m_yLabel = label; } /*! \brief Returns the ordinates label for the mass spectrum data. */ QString MassDataCborMassSpectrumHandler::getYLabel() const { return m_yLabel; } /*! \brief Sets the mass spectrum data to \a trace. */ void MassDataCborMassSpectrumHandler::setTrace(const pappso::Trace &trace) { m_trace = trace; } /*! \brief Returns the mass spectrum data. */ pappso::Trace MassDataCborMassSpectrumHandler::getTrace() const { return m_trace; }; /*! \brief Clears the mass spectrum data. */ void MassDataCborMassSpectrumHandler::clearTrace() { m_trace.clear(); } /*! \brief Sets the color with which the mass spectrum data must be displayed. The color i encoded as the \a color_byte_array byte array. */ void MassDataCborMassSpectrumHandler::setTraceColor( const QByteArray &color_byte_array) { m_colorByteArray = color_byte_array; } /*! \brief Returns the color with which the mass spectrum data must be displayed. */ QByteArray MassDataCborMassSpectrumHandler::getTraceColor() const { return m_colorByteArray; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassDataClient.cpp000664 001750 001750 00000022347 15100504560 025633 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ***************************************************************************** * This specific code is a port to C++ of the Envemind Python code by Radzinski * and colleagues of IsoSpec fame (Lacki, Startek and company :-) * * See https://github.com/PiotrRadzinski/envemind. * ***************************************************************************** * * END software license */ #include #include #include "MsXpS/libXpertMassCore/MassDataClient.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassDataClient \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MassDataClient.hpp \brief The MassDataClient class provides a network client. */ /*! \variable MsXpS::libXpertMassCore::MassDataClient::m_inStream \brief The data stream in input into this MassDataClient instance. */ /*! \variable MsXpS::libXpertMassCore::MassDataClient::m_data \brief The data being transported. */ /*! \variable MsXpS::libXpertMassCore::MassDataClient::m_ipAddress \brief The server IP address to which the connection must be made. */ /*! \variable MsXpS::libXpertMassCore::MassDataClient::m_portNumber \brief The port number to which to attach during the connection. */ /*! \variable MsXpS::libXpertMassCore::MassDataClient::mp_tcpSocket \brief The socket that is used for the connection to the server. */ /*! * \variable MsXpS::libXpertMassCore::MassDataClient::m_forcefulDisconnection * * \brief Tells if the disconnection was forceful. */ /*! \brief Constructs a MassDataClient instance. \list \li \a ip_address: the server (\l{MassDataServer}) IP address at which the client needs to try a connection. \li \a port_number: the port number for the connection. \li \a parent: the parent QObject. \endlist */ MassDataClient::MassDataClient(const QString &ip_address, int port_number, QObject *parent) : QObject(parent), m_ipAddress(ip_address), m_portNumber(port_number) { mp_tcpSocket = new QTcpSocket(parent); if(mp_tcpSocket == nullptr) qFatal("Failed to allocate a socket."); // When the connection below (see call mpa_tcpSocket->connectToHost()) // will actually work, this signal will be emitted and we respond // by sending to the socket the READY string so that the server // know we are ready to receive data. connect(mp_tcpSocket, &QTcpSocket::connected, [this]() { mp_tcpSocket->write("READY\n"); mp_tcpSocket->flush(); emit connectedSignal(); }); connect(mp_tcpSocket, &QTcpSocket::hostFound, [this]() { emit hostFoundSignal(); }); connect(mp_tcpSocket, &QAbstractSocket::errorOccurred, this, &MassDataClient::reportError); connect(mp_tcpSocket, &QTcpSocket::disconnected, [this]() { // This signal can be sent when the user forcefully // disconnects from the host. In this case, the socket // is destroyed. So we need to ensure we only try to // reconnect if the disconnection was not voluntary. if(mp_tcpSocket != nullptr) { qDebug() << "Lost connection, the socket is not destroyed." << QDateTime::currentDateTime(); if(m_forcefulDisconnection) { qDebug() << "The disconnection was forceful. Do not restart." << QDateTime::currentDateTime(); } else { qDebug() << "The disconnection was not forceful, retrying " "connection in 5s..." << QDateTime::currentDateTime() << mp_tcpSocket; QTimer::singleShot(5000, [this]() { mp_tcpSocket->connectToHost(m_ipAddress, m_portNumber); }); } } else { qDebug() << "The socket was destroyed already. Doing nothing." << QDateTime::currentDateTime() << QDateTime::currentDateTime(); } qDebug() << "Now emitting disconnectedSignal() for the user of this client." << QDateTime::currentDateTime(); emit disconnectedSignal(); }); // When data will be ready to read from the server, they well be served // in this m_inStream. m_inStream.setDevice(mp_tcpSocket); m_inStream.setVersion(QDataStream::Qt_5_0); connect(mp_tcpSocket, &QIODevice::readyRead, this, &MassDataClient::readData); mp_tcpSocket->connectToHost(ip_address, port_number); } /*! \brief Destructs this MassDataClient instance. */ MassDataClient::~MassDataClient() { } /*! \brief Reads the data into the QByteArray m_data member using the QDataStream m_inStream member. Emits the newDataSignal() signal with m_data;. */ void MassDataClient::readData() { // qDebug() << "Reading data."; // We need to have the same version, FIXME, this should go as a macro // somewhere. // qDebug() << "The number of bytes available in this readData call:" //<< mp_tcpSocket->bytesAvailable() << "at" //<< QDateTime::currentDateTime().toString(); // This version uses the readAll() function. // QByteArray byte_array = mp_tcpSocket->readAll(); // QDataStream stream(byte_array); // stream.setVersion(QDataStream::Qt_5_0); // stream.startTransaction(); // stream >> m_data; // if(!stream.commitTransaction()) //{ // qDebug() << "Failed to commit the data read transaction."; // return ; //} // This version uses the stream operator. // Seems to work equivalently to the version above. // stream.setVersion(QDataStream::Qt_5_0); m_inStream.startTransaction(); m_inStream >> m_data; // qDebug() << "Atomically read" << m_data.size(); if(!m_inStream.commitTransaction()) { qDebug() << "Could NOT commit the transaction fine."; return; } // else // qDebug() << "Could commit the transaction fine."; emit newDataSignal(m_data); // qDebug() << "Got these data:" << QString(m_data); } /*! \brief Reports the error \a socket_error. */ void MassDataClient::reportError(QAbstractSocket::SocketError socket_error) { switch(socket_error) { case QAbstractSocket::RemoteHostClosedError: // qDebug() << "Error: QAbstractSocket::RemoteHostClosedError."; // emit reportErrorSignal("RemoteHostClosedError"); break; case QAbstractSocket::HostNotFoundError: // qDebug() << "Error: QAbstractSocket::HostNotFoundError."; // emit reportErrorSignal("HostNotFoundError"); break; case QAbstractSocket::ConnectionRefusedError: qDebug() << "Error: QAbstractSocket::ConnectionRefusedError."; emit reportErrorSignal("ConnectionRefusedError"); break; default: // qDebug() << "Error:" << mp_tcpSocket->errorString(); // emit reportErrorSignal(mp_tcpSocket->errorString()); break; } } void MassDataClient::forcefullyDisconnect() { qDebug() << "Forcefully disconnecting client." << QDateTime::currentDateTime(); m_forcefulDisconnection = true; if(mp_tcpSocket != nullptr) { // Maybe we do not need this step, deleting the socket will // emit the disconnection signal. // mp_tcpSocket->disconnectFromHost(); qDebug() << "Deleting the socket that was disconnected" << QDateTime::currentDateTime() << mp_tcpSocket; delete mp_tcpSocket; mp_tcpSocket = nullptr; qDebug() << "Deletedg the socket that was disconnected" << QDateTime::currentDateTime() << mp_tcpSocket; // This will emit the disconnected() signal that we have // connected to the emission of disconnectedSignal(). // The user of this MassDataClient instance should connect // a slot this disconnectedSignal() and handle the destruction of // this very instance. } } QString MassDataClient::getIpAddress() const { return m_ipAddress; } int MassDataClient::getPortNumber() const { return m_portNumber; } /*! \brief Returns a string display the connection details. */ QString MassDataClient::getStatus() { return QString("Doing well, connection should be to IP address: %1, port %2.") .arg(m_ipAddress) .arg(m_portNumber); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassDataServer.cpp000664 001750 001750 00000017406 15100504560 025663 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * ***************************************************************************** * This specific code is a port to C++ of the Envemind Python code by Radzinski * and colleagues of IsoSpec fame (Lacki, Startek and company :-) * * See https://github.com/PiotrRadzinski/envemind. * ***************************************************************************** * * END software license */ #include #include #include #include "MsXpS/libXpertMassCore/MassDataServer.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassDataServer \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MassDataServer.hpp \brief The MassDataServer class provides a network server. */ /*! \variable MsXpS::libXpertMassCore::MassDataServer::m_ipAddress \brief The IP address at which this server serves data. */ /*! * \variable MsXpS::libXpertMassCore::MassDataServer::m_portNumber * * \brief The port number at which this server serves data. */ /*! * \variable MsXpS::libXpertMassCore::MassDataServer::m_clients * * \brief The list of QTcpSocket instances representing the clients connected to * this server. */ /*! * \variable MsXpS::libXpertMassCore::MassDataServer::m_readyClients * * \brief The list of QTcpSocket instances representing the clients connected to * this server and effectively expecting data from this server. */ /*! \brief Constructs a MassDataServer instance. \list \li \a parent: QObject parent. \endlist */ MassDataServer::MassDataServer(QObject *parent): QTcpServer(parent) { } /*! \brief Destructs this MassDataServer instance. */ MassDataServer::~MassDataServer() { } bool MassDataServer::start() { if(!listen()) { qDebug() << "Failed to start the server."; return false; } // At this point try to get to the details. QList ip_addressesList = QNetworkInterface::allAddresses(); // Use the first non-localhost IPv4 address for(int i = 0; i < ip_addressesList.size(); ++i) { if(ip_addressesList.at(i) != QHostAddress::LocalHost && ip_addressesList.at(i).toIPv4Address()) { m_ipAddress = ip_addressesList.at(i).toString(); break; } } // If we did not find one, use IPv4 localhost if(m_ipAddress.isEmpty()) m_ipAddress = QHostAddress(QHostAddress::LocalHost).toString(); m_portNumber = serverPort(); return true; } QString MassDataServer::getIpAddress() const { return m_ipAddress; } int MassDataServer::getPortNumber() const { return m_portNumber; } /*! \brief Sets to this MassDataServer instance the data to be served in \a byte_array_to_serve. */ void MassDataServer::serveData(const QByteArray &byte_array_to_serve) { if(!byte_array_to_serve.size()) return; for(QTcpSocket *client_p : m_readyClients) { if(client_p->state() == QAbstractSocket::ConnectedState) { QByteArray byte_array; QDataStream out_stream(&byte_array, QIODevice::WriteOnly); out_stream.setVersion(QDataStream::Qt_5_0); out_stream << byte_array_to_serve; qDebug() << "On the verge of writing byte_array of size:" << byte_array.size(); int written_bytes = client_p->write(byte_array); client_p->flush(); qDebug() << "Now written " << written_bytes << " bytes to the socket at" << QDateTime::currentDateTime(); if(written_bytes >= byte_array.size()) { qDebug() << "Data successfully written."; emit writtenDataSignal(written_bytes); } else { qWarning() << "The data written to the socket were less than the " "initial data."; } } } } /*! \brief Handles an incoming connection with socket descriptor \a socket_descriptor. If the connection is effective, that is, if a QTcpSocket client could be allocated and configured with \a socket_descriptor, that client is appended to the \l{m_clients} list of clients. */ void MassDataServer::incomingConnection(qintptr socket_descriptor) { // It is not because we have not data to use as a response to the caller // that we do not perform the connection and then closing stuff! Otherwise we // consume a file descriptor (the socket) each time a connection is tried // here from the client.... Bug that has broken my head for weeks... QTcpSocket *client_tcp_socket_p = new QTcpSocket(this); if(!client_tcp_socket_p->setSocketDescriptor(socket_descriptor)) { delete client_tcp_socket_p; return; } qDebug() << "MineXpert3 connected from" << client_tcp_socket_p->peerAddress().toString() << ":" << client_tcp_socket_p->peerPort(); connect(client_tcp_socket_p, &QTcpSocket::disconnected, this, [this, client_tcp_socket_p]() { qDebug() << "One client has disconnected, removing it."; qDebug() << "Before removing, there are" << m_readyClients.size() << "ready clients."; m_readyClients.remove(client_tcp_socket_p); qDebug() << "Now remaining" << m_readyClients.size() << "ready clients."; qDebug() << "Before removing, there are" << m_clients.size() << "clients."; m_clients.removeOne(client_tcp_socket_p); qDebug() << "Now remaining" << m_clients.size() << "clients."; client_tcp_socket_p->deleteLater(); }); // When the client connects, it sends a "READY" message that we catch // to unambiguously determine that the client is connected. connect(client_tcp_socket_p, &QTcpSocket::readyRead, this, [this, client_tcp_socket_p]() { QByteArray msg = client_tcp_socket_p->readAll(); if(msg.contains("READY")) { qDebug() << "MineXpert3 is ready!"; m_readyClients.insert(client_tcp_socket_p); } }); m_clients.append(client_tcp_socket_p); qDebug() << "MineXpert3 connected"; m_clients.append(client_tcp_socket_p); } /*! \brief Returns true if this server has clients (\l{MassDataClient}) that are ready to receive data. */ bool MassDataServer::hasReadyClient() const { return !m_readyClients.isEmpty(); } /*! \brief Reports the \a socket_error to the console using qDebug(). */ void MassDataServer::error(QTcpSocket::SocketError socket_error) { qDebug() << "An error occurred in the mass data server:" << socket_error; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassPeakShaper.cpp000664 001750 001750 00000032337 15100504560 025646 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MassPeakShaper \inmodule libXpertMassCore \ingroup XpertMassCoreMassCalculations \inheaderfile MassPeakShaper.hpp \brief The MassPeakShaper class provides the features needed to shape a mass peak. \e{Shaping a peak} means creating a shape around a centroid m/z value such that the m/z peak is represented like it appears in a mass spectrum displayed in "profile" mode. The configuration of the mass peak shaping is held in a specific \l MassPeakShaperConfig class. \sa MassPeakShaperConfig */ /*! \typedef MsXpS::libXpertMassCore::MassPeakShaperSPtr \relates MassPeakShaper Synonym for std::shared_ptr. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaper::m_peakCentroid \brief The peak centroid for which a shape is computed. A peak centroid is the center of a mass peak profile and is thus the (m/z, intensity) pair. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaper::m_config \brief The configuration needed to drive the mass peak shaping process. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaper::m_trace \brief The Trace object that will receive the different points that make the peak shape. */ /*! \brief Constructs a MassPeakShaper instance. */ MassPeakShaper::MassPeakShaper() : m_peakCentroid(0, 0) { } /*! \brief Constructs a MassPeakShaper instance. \list \li \a mz: The peak centroid m/z value. \li \a intensity: The peak centroid intensity value. \li \a config: The configuration driving the mass peak shaping process. \endlist */ MassPeakShaper::MassPeakShaper(double mz, double intensity, const MassPeakShaperConfig &config) : m_peakCentroid(mz, intensity), m_config(config) { } /*! \brief Constructs a MassPeakShaper instance. \list \li \a data_point: The data point representing the peak centroid. \li \a config: The configuration driving the mass peak shaping process. \endlist */ MassPeakShaper::MassPeakShaper(const pappso::DataPoint &data_point, const MassPeakShaperConfig &config) : m_peakCentroid(data_point), m_config(config) { // qDebug()"m_config:" << m_config.asText(800); } /*! \brief Constructs a MassPeakShaper instance as a copy of \a other. */ MassPeakShaper::MassPeakShaper(const MassPeakShaper &other) : m_peakCentroid(other.m_peakCentroid), m_config(other.m_config), m_trace(other.m_trace) { } /*! \brief Destructs this MassPeakShaper instance. */ MassPeakShaper::~MassPeakShaper() { } /*! \brief Sets the \a peak_centroid_data. */ void MassPeakShaper::setPeakCentroid(const pappso::DataPoint &peak_centroid_data) { m_peakCentroid = peak_centroid_data; } /*! \brief Returns the peak centroid data. */ const pappso::DataPoint & MassPeakShaper::getPeakCentroid() const { return m_peakCentroid; } /*! \brief Returns the peak shape as a pappso::Trace. */ const pappso::Trace & MassPeakShaper::getTrace() const { return m_trace; } /* \brief Clears the peak shape. */ void MassPeakShaper::clearTrace() { m_trace.clear(); } /*! \brief Sets the configuration driving the peak shaping process to \a config. */ void MassPeakShaper::setConfig(const MassPeakShaperConfig &config) { m_config.initialize(config); } /*! \brief Returns the configuration driving the peak shaping process. */ const MassPeakShaperConfig & MassPeakShaper::getConfig() const { return m_config; } /*! \brief Computes the peak shape of the peak centroid. Returns the count of points in the peak shape. */ int MassPeakShaper::computePeakShape() { if(m_config.getMassPeakShapeType() == Enums::MassPeakShapeType::GAUSSIAN) return computeGaussianPeakShape(); else return computeLorentzianPeakShape(); } /*! \brief Computes the peak shape of the peak centroid. \list \li \a mz: the peak centroid's m/z value. \li \a intensity: the peak centroid's intensity value. \li \a config: the configuration driving the peak shaping process. \endlist Returns the pappso::Trace describing the peak shape. */ pappso::Trace MassPeakShaper::computePeakShape(double mz, double intensity, const MassPeakShaperConfig &config) { if(config.getMassPeakShapeType() == Enums::MassPeakShapeType::GAUSSIAN) return computeGaussianPeakShape(mz, intensity, config); else return computeLorentzianPeakShape(mz, intensity, config); } /*! \brief Computes the Gaussian peak shape of the peak centroid. */ int MassPeakShaper::computeGaussianPeakShape() { // qDebug(); m_trace.clear(); m_trace = computeGaussianPeakShape(m_peakCentroid.x, m_peakCentroid.y, m_config); return m_trace.size(); } /*! \brief Computes the Gaussian peak shape of the peak centroid. \list \li \a mz: the peak centroid's m/z value. \li \a intensity: the peak centroid's intensity value. \li \a config: the configuration driving the peak shaping process. \endlist Returns the pappso::Trace describing the peak shape. */ pappso::Trace MassPeakShaper::computeGaussianPeakShape(double mz, double intensity, const MassPeakShaperConfig &config) { pappso::Trace trace; // We will use the data in the configuration object. First check that // we can rely on it. This call sets all the proper values to the m_config's // member data after having validate each. MassPeakShaperConfig local_config; local_config.initialize(config); ErrorList error_list; if(!local_config.resolve(error_list)) { qDebug() << "Failed to resolve the MassPeakShaperConfig with errors:\n" << Utils::joinErrorList(error_list, "\n"); return trace; } // qDebug() << "The peak shaper configuration:" << m_config.toString(); // First off, we need to tell what the height of the gaussian peak should // be. double a; // a = m_config.a(mz); // We actually set a to 1, because it is the intensity above that will // provide the height of the peak, see below where the height of the peak is // set to a * intensity, that is, intensity if a = 1. a = 1; // qDebug() << "a:" << a; bool ok = false; double c = local_config.c(&ok); if(!ok) { return trace; } double c_square = c * c; // qDebug() << "c:" << c << "c²:" << c_square; // Were are the left and right points of the shape ? We have to // determine that using the point count and mz step values. // Compute the mz step that will separate two consecutive points of the // shape. This mzStep is function of the number of points we want for a // given peak shape and the width of the peak shape left and right of the // centroid. double mz_step = local_config.getMzStep(); double left_point = mz - ((double)FWHM_PEAK_SPAN_FACTOR / 2 * local_config.getFwhm()); double right_point = mz + ((double)FWHM_PEAK_SPAN_FACTOR / 2 * local_config.getFwhm()); // qDebug() << "left m/z:" << left_point; // qDebug() << "right m/z:" << right_point; int iterations = (right_point - left_point) / mz_step; double x = left_point; for(int iter = 0; iter < iterations; ++iter) { double y = intensity * a * exp(-1 * (pow((x - mz), 2) / (2 * c_square))); trace.push_back(pappso::DataPoint(x, y)); x += mz_step; } // qDebug() << qSetRealNumberPrecision(15) << "For centroid" << mz //<< "first shape point:" << left_point //<< "with trace:" << trace.toString(); return trace; } /*! \brief Computes the Lorentzian peak shape of the peak centroid. */ int MassPeakShaper::computeLorentzianPeakShape() { // qDebug(); m_trace.clear(); m_trace = computeLorentzianPeakShape(m_peakCentroid.x, m_peakCentroid.y, m_config); return m_trace.size(); } /*! \brief Computes the Lorentzian peak shape of the peak centroid. \list \li \a mz: the peak centroid's m/z value. \li \a intensity: the peak centroid's intensity value. \li \a config: the configuration driving the peak shaping process. \endlist Returns the pappso::Trace describing the peak shape. */ pappso::Trace MassPeakShaper::computeLorentzianPeakShape(double mz, double intensity, const MassPeakShaperConfig &config) { pappso::Trace trace; // We will use the data in the configuration object. First check that // we can rely on it. This call sets all the proper values to the m_config's // member data after having validate each. MassPeakShaperConfig local_config; local_config.initialize(config); ErrorList error_list; if(!local_config.resolve(error_list)) { qDebug() << "Failed to resolve the MassPeakShaperConfig with errors:\n" << Utils::joinErrorList(error_list, "\n"); return trace; } // qDebug() << "The peak shaper configuration:" << m_config.toString(); // First off, we need to tell what the height of the gaussian peak should // be. double a; // a = local_config.a(mz); // We actually set a to 1, because it is the intensity above that will // provide the height of the peak, see below where the heigh of the peak is // set to a * intensity, that is, intensity if a = 1. a = 1; // qDebug() << "a value:" << a; bool ok = false; // The calls below will trigger the computation of fwhm, if it is // equal to 0 because it was not set manually. double gamma = local_config.gamma(&ok); if(!ok) { return trace; } double gamma_square = gamma * gamma; // qDebug() << "gamma:" << gamma << "gamma²:" << gamma_square; // Were are the left and right points of the shape ? We have to // determine that using the m_points and m_increment values. // Compute the mz step that will separate two consecutive points of the // shape. This mzStep is function of the number of points we want for a // given peak shape and the width of the peak shape left and right of the // centroid. double mz_step = local_config.getMzStep(); double left_point = mz - ((double)FWHM_PEAK_SPAN_FACTOR / 2 * local_config.getFwhm()); double right_point = mz + ((double)FWHM_PEAK_SPAN_FACTOR / 2 * local_config.getFwhm()); // qDebug() << "left m/z:" << left_point; // qDebug() << "right m/z:" << right_point; int iterations = (right_point - left_point) / mz_step; double x = left_point; for(int iter = 0; iter < iterations; ++iter) { double y = intensity * a * (gamma_square / (pow((x - mz), 2) + gamma_square)); trace.push_back(pappso::DataPoint(x, y)); x += mz_step; } // qDebug().noquote() << m_trace.toString(); return trace; } /*! \brief Returns the intensity of a data point in the member peak shape pappso::Trace (m_trace). If the member pappso::Trace contains a data point having its x value equal to \a mz (comparison performed using tolerance \a precision_p), then return the intensity (y member) of that data point and set \a ok to true. Otherwise, return 0 and set \a ok to false. */ double MassPeakShaper::intensityAt(double mz, pappso::PrecisionPtr precision_p, bool &ok) { pappso::DataPoint data_point = m_trace.containsX(mz, precision_p); if(data_point.isValid()) { ok = true; return data_point.y; } ok = false; return 0; } /*! \brief Returns a string with the data in the member pappso::Trace. */ QString MassPeakShaper::shapetoString() { return m_trace.toString(); } /*! \brief Writes to \a file_name a string containing the member pappso::Trace data. Returns true if successful, false otherwise. \sa shapetoString() */ bool MassPeakShaper::shapeToFile(const QString &file_name) { return pappso::Utils::writeToFile(m_trace.toString(), file_name); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MassPeakShaperConfig.cpp000664 001750 001750 00000064337 15100504560 027001 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \variable MsXpS::libXpertMassCore::FWHM_PEAK_SPAN_FACTOR \brief The compounding factor to account for when shaping the sides of the peak. The shape of the peak needs to reflect a real mass peak. In particular, the shape of the peak has to return, \e{on each one of both sides}, to the baseline, thus mimicking the baseline for a m/z distance equivalent to FWHM_PEAK_SPAN_FACTOR times the FWHM m/z range from left to right. That means that the simulated peak region at the left hand side of the centroid value will span (FWHM_PEAK_SPAN_FACTOR/2) times the FWHM and the same one right. Empirically, a good FWHM_PEAK_SPAN_FACTOR is \c 4, meaning that the left half of the peak (that is centered on the centroid) will have a size corresponding to two times the FHWM, and the same for the right half of the peak. This is best exemplified as follows: \code Centroid value ^ | [ m/z - (2 * FWHM) <-|-> m/z + (2 * FWHM) ] <----- width of the whole peak shape -----> \endcode */ int FWHM_PEAK_SPAN_FACTOR = 4; /*! \class MsXpS::libXpertMassCore::MassPeakShaperConfig \inmodule libXpertMassCore \ingroup XpertMassCoreMassCalculations \inheaderfile MassPeakShaperConfig.hpp \brief The MassPeakShaperConfig class provides the features required to configure the shaping of a mass peak centroid into a Gaussian or a Lorentzian fully profiled shape. \e{Shaping a peak} means creating a shape around a centroid m/z value such that the m/z peak is represented like it appears in a mass spectrum displayed in "profile" mode. \details{The peak shaping process} The model used to craft the "profiled" mass peak can be of two types: \list \li \l{https://en.wikipedia.org/wiki/Gaussian_function}{Gaussian} \li \l{https://en.wikipedia.org/wiki/Cauchy_distribution}{Lorentzian} \endlist The starting parameters are the (m/z,intensity) values of the peak centroid and the full width at half maximum (FWHM) value that will govern the width of the shape at 50% height of the peak. The width of the peak will be an inverse representation of the resolving power of the instrument: the instrument with the greatest resolving power will yield mass peaks with the smallest width. Conversely, a very old instrument will produce mass spectra where the peaks will be broad. There are thus two possiblities for configuring the peak shape: \list \li Provide the FWHM value itself. \li Provide the resolving power of the instrument that the peak simulation should emulate. \endlist \enddetails */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_resolution \brief Resolving power of the instrument. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_fwhm \brief Full width at half maximum of the shaped peak. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_massPeakWidthLogic \brief Describes the logic used to compute the peak shape width. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_referencePeakMz \brief The m/z value to be used when computing the FWHM value starting from a mass spectrometer resolving power. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_pointCount \brief The count of points used to shape the peak. Typically between 150 and 300. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_withBins \brief Tells if bins are requested for the computation. There is no need in the context of the calculations performed by the MassPeakShaperConfig class to know if bins are required or not. This variable is useful when multiple peak shapes are combined into a more complex trace or mass spectrum, typically when simulating an isotopic cluster. In this case, each cluster peak centroid is shaped in sequence and then they are all merged into a single trace (no bins) or mass spectrum (binning). The width of the bins is defined using this m_withBins variable. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_binSizeDivisor \brief Empirical division factor to apply to \l m_binSize to reduce the size of the bins after the bin size calculation. Empirically, a good value is \c 6. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_binSize \brief The size of the m/z bins. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_isBinSizeFixed \brief Tells if the size of the m/z bins is fixed. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_mzStep \brief The m/z distance (a Delta) between to consecutive data points in the shaped mass peak. */ /*! \variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_massPeakShapeType \brief The requested shape of the mass peak. */ /*! \brief Constructs a MassPeakShaperConfig instance. */ MassPeakShaperConfig::MassPeakShaperConfig(QObject *parent): QObject(parent) { reset(); } /*! \brief Constructs a MassPeakShaperConfig instance as a copy of \a other. */ MassPeakShaperConfig::MassPeakShaperConfig(const MassPeakShaperConfig &other, QObject *parent) : QObject(parent), m_resolution(other.m_resolution), m_fwhm(other.m_fwhm), m_massPeakWidthLogic(other.m_massPeakWidthLogic), m_referencePeakMz(other.m_referencePeakMz), m_pointCount(other.m_pointCount), m_withBins(other.m_withBins), m_binSizeDivisor(other.m_binSizeDivisor), m_binSize(other.m_binSize), m_isBinSizeFixed(other.m_isBinSizeFixed), m_mzStep(other.m_mzStep), m_massPeakShapeType(other.m_massPeakShapeType) { } /*! \brief Destructs this MassPeakShaperConfig instance. */ MassPeakShaperConfig::~MassPeakShaperConfig() { } /*! \brief Assigns \a other to this MassPeakShaperConfig instance. */ MassPeakShaperConfig & MassPeakShaperConfig::initialize(const MassPeakShaperConfig &other) { if(this == &other) return *this; m_resolution = other.m_resolution; m_fwhm = other.m_fwhm; m_massPeakWidthLogic = other.m_massPeakWidthLogic; m_referencePeakMz = other.m_referencePeakMz; m_pointCount = other.m_pointCount; m_withBins = other.m_withBins; m_binSizeDivisor = other.m_binSizeDivisor; m_binSize = other.m_binSize; m_isBinSizeFixed = other.m_isBinSizeFixed; m_mzStep = other.m_mzStep; m_massPeakShapeType = other.m_massPeakShapeType; return *this; } /*! \brief Returns a newly allocated MassPeakShaperConfig instance initialized using \a other and with parent set to \a parent. */ MassPeakShaperConfig * MassPeakShaperConfig::clone(const MassPeakShaperConfig &other, QObject *parent) const { MassPeakShaperConfig *copy_p = new MassPeakShaperConfig(parent); copy_p->initialize(other); return copy_p; } /*! \brief Sets the \a resolution (the resolving power of the instrument). */ void MassPeakShaperConfig::setResolution(int resolution) { m_resolution = resolution; } /*! \brief Returns the resolution (the resolving power of the instrument). */ double MassPeakShaperConfig::getResolution() const { return m_resolution; } /*! \brief Calculates the resolution (resolving power of the instrument). The calculation involves using the FWHM (m_fwhm) member datum and the m/z value that is considered to be the reference for the calculation (m_referencePeakMz). If any of these two values is not set, then this function returns 0 and sets \a ok to false. The calculation is that simple: \code m_resolution = m_referencePeakMz / m_fwhm; \endcode Because we used the FWHM to compute the resolution and set the calculated value to the \l m_resolution member datum, we keep a record of this by: \code m_massPeakWidthLogic = Enums::MassPeakWidthLogic::FWHM; \endcode Returns the resolving power as set in \l m_resolution and sets \a ok to true. */ int MassPeakShaperConfig::resolution(bool *ok) { if(ok == nullptr) qFatal("The pointer cannot be nullptr."); // If we want to compute the resolution that means that we have to have // m_fwhm. if(!m_fwhm) { *ok = false; return 0; } if(!m_referencePeakMz) { *ok = false; return 0; } m_resolution = m_referencePeakMz / m_fwhm; *ok = true; // We used the FWHM to compute the resolving power. m_massPeakWidthLogic = Enums::MassPeakWidthLogic::FWHM; return m_resolution; } /*! \brief Sets the \a fwhm (full width at half maximum). */ void MassPeakShaperConfig::setFwhm(double fwhm) { m_fwhm = fwhm; } /*! \brief Gets the fwhm (full width at half maximum). */ double MassPeakShaperConfig::getFwhm() const { return m_fwhm; } /*! \brief Calculates the fwhm (full width at half maximum). The calculation involves using the instrument's resolving power (m_resolution) member datum and the m/z value that is considered to be the reference for the calculation (m_referencePeakMz). If any of these two values is not set, then this function returns 0 and sets \a ok to false. The calculation is that simple: \code m_fwhm = m_referencePeakMz / m_resolution; \endcode Because we used the resolution to compute the FWHM and set the calculated value to the \l m_fwhm member datum, we keep a record of this by: \code m_massPeakWidthLogic = Enums::MassPeakWidthLogic::RESOLUTION; \endcode Returns the FWHM value as set in \l m_fwhm and sets \a ok to true. */ double MassPeakShaperConfig::fwhm(bool *ok) { if(ok == nullptr) qFatal("The pointer cannot be nullptr."); // Or we need to compute it using the mz passed as parameter and the // resolution. if(!m_resolution) { *ok = false; return 0; } if(!m_referencePeakMz) { // qDebug() << "There is no reference peak centroid!"; *ok = false; return 0; } m_fwhm = m_referencePeakMz / m_resolution; *ok = true; // We used the resolving power to compute the FWHM. m_massPeakWidthLogic = Enums::MassPeakWidthLogic::RESOLUTION; return m_fwhm; } /*! \brief Returns the half of the FWMH (m_fwhm) for use in the Lorentzian shape calculation. Uses the \l fwhm() function to compute the FWHM value. If the FWHM calculation fails, returns 0 and \a ok is set to false, otherwise returns half of FWHM and \a ok is set to true. \sa fwhm() */ double MassPeakShaperConfig::halfFwhm(bool *ok) { double fwhm_value = fwhm(ok); if(!*ok) return 0; return (fwhm_value / 2); } /*! \brief Sets the reference m/z value required for calculations to \a mz. */ void MassPeakShaperConfig::setReferencePeakMz(double mz) { m_referencePeakMz = mz; } /*! \brief Gets the reference m/z value required for calculations. */ double MassPeakShaperConfig::getReferencePeakMz() const { return m_referencePeakMz; } /*! \brief Sets the requirement to prepare m/z bins at a later computing stage to \a with_bins. \sa IsotopicClusterShaper::run(), isWithBins() */ void MassPeakShaperConfig::setWithBins(bool with_bins) { m_withBins = with_bins; } /*! \brief Returns the requirement to prepare m/z bins at a later computing stage. \sa IsotopicClusterShaper::run(), setWithBins() */ bool MassPeakShaperConfig::isWithBins() const { return m_withBins; } /*! \brief Sets the \a bin_size. */ void MassPeakShaperConfig::setBinSize(double bin_size) { m_binSize = bin_size; } /*! \brief Computes the bin size. If there is no requirement for bins (m_withBins is false), returns 0 and set \a ok to true. In order to compute the bin size we need that at least the FWHM (m_fwhm) or the resolving power (m_resolution) be set. If none of these values are set, this function returns 0 and sets \a ok to false. If the bin size was fixed (m_isBinSizeFixed is true), then the bin size (m_binSize) must be set. If that is not the case, the function returns 0 and sets \a ok to false. If the FWHM value (m_fwhm) is set, then it is used right away. Otherwise, the FWHM is computed starting from the resolving power (m_resolution). If that computation fails, the function returns 0 and sets \a ok to false. Starting from the FWHM value (m_fwhm), the bin size calculation requires that the m_binSizeDivisor be > 1. The bin size is thus computed as the ratio FWHM / m_binSizeDivisor, and the obtained value is set to m_binSize. Empirically, a value of 6 for m_binSizeDivisor, yields bin sizes that allow combining correctly multiple peak shapes into a mass spectrum. Essentially, that means that 6 individual m/z bins are required to combine nicely all the peaks shapes obtained for all the peak centroids in a given isotopic cluster, for example. At this point, the function returns the bin size and sets \a ok to true. \sa setBinSizeDivisor() */ double MassPeakShaperConfig::binSize(bool *ok) { if(ok == nullptr) qFatal("The pointer cannot be nullptr."); // If the bin size was set before, then, just return it. // if(m_binSize) //{ //*ok = true; // return m_binSize; //} if(!m_withBins) { // qDebug() << "Bins are not requested, just return 0 and set true."; *ok = true; return 0; } // In order to compute the bin Size, we need the FWHM and the number of // points. if(!m_resolution && !m_fwhm) { // qDebug() << "That's an error when neither resolution nor FWHM is set."; *ok = false; return 0; } if(m_fwhm) { // FWHM is fine, we can use that immediately. // qDebug() << "FWHM:" << m_fwhm; if(!m_pointCount) { // qDebug() << "That's an error that the point count is 0."; *ok = false; return 0; } } else { // We have to work with the resolution. // qDebug() << "Resolution:" << m_resolution; fwhm(ok); if(!*ok) { // qDebug() //<< "Could not compute FWHM on the basis of the resolution."; if(!m_pointCount) { // qDebug() << "That's an error that the point count is 0."; *ok = false; return 0; } } } if(m_isBinSizeFixed) { // The bin size has to be set and must not be changed, as the user has // set it manually. if(!m_binSize) { // qDebug() << "The bin size should be set manually but is not set."; *ok = false; return 0; } } else { m_binSize = m_fwhm / m_binSizeDivisor; // qDebug() << "The bin size was computed:" << m_binSize; } *ok = true; return m_binSize; } /*! \brief Returns (no computation) the bin size. */ double MassPeakShaperConfig::getBinSize() const { return m_binSize; } /*! \brief Sets the requirement that the bin size be set and fixed (not modifiable by computations) to \a is_fixed. When \a is_fixed is true, the \l binSize() function returns the bin size without trying to compute it. \sa binSize() */ void MassPeakShaperConfig::setBinSizeFixed(bool is_fixed) { m_isBinSizeFixed = is_fixed; } /* \brief Gets the requirement that the bin size be set and fixed (not modifiable by computations). */ bool MassPeakShaperConfig::getBinSizeFixed() { return m_isBinSizeFixed; } /*! \brief Sets the bin size computation dividing \a factor. When the bin size is computed (m_isBinSizeFixed is false), that value is determined by dividing the FWHM value by this \a factor (m_binSizeDivisor). Empirically, a value of 6 for m_binSizeDivisor, yields bin sizes that allow combining correctly multiple peak shapes into a mass spectrum. Essentially, that means that 6 individual m/z bins are required to combine nicely all the peaks shapes obtained for all the peak centroids in a given isotopic cluster, for example. */ void MassPeakShaperConfig::setBinSizeDivisor(int factor) { if(std::abs(factor) < 1) qFatal("Programming error."); m_binSizeDivisor = std::abs(factor); } /*! \brief Returns the bin size computation dividing factor. \sa setBinSizeDivisor() */ int MassPeakShaperConfig::getBinSizeDivisor() const { return m_binSizeDivisor; } /*! \brief Sets the \a point_count to be used to shape the mass peak around the m/z centroid value. Typically, a value between 150 and 200 points is fine to nicely shape a m/z peak centroid. */ void MassPeakShaperConfig::setPointCount(int point_count) { m_pointCount = point_count; } /*! \brief Gets the count of points to be used to shape the mass peak around the m/z centroid value. */ int MassPeakShaperConfig::getPointCount() const { return m_pointCount; } /*! \brief Sets the type of mass peak shape to \a mass_peak_shape_type. \sa MsXpS::libXpertMassCore::Enums::MassPeakShapeType */ void MassPeakShaperConfig::setMassPeakShapeType( Enums::MassPeakShapeType mass_peak_shape_type) { m_massPeakShapeType = mass_peak_shape_type; } /*! \brief Gets the type of mass peak shape. \sa MsXpS::libXpertMassCore::Enums::MassPeakShapeType */ Enums::MassPeakShapeType MassPeakShaperConfig::getMassPeakShapeType() const { return m_massPeakShapeType; } /*! \brief Sets the mass peak width calculation \a logic. The full mass peak width at half peak maximum (FWHM) can either be set manually or be computed from a reference m/z value and the instrument's resolving power. \sa MsXpS::libXpertMassCore::Enums::MassPeakWidthLogic */ void MassPeakShaperConfig::setMassPeakWidthLogic(Enums::MassPeakWidthLogic logic) { m_massPeakWidthLogic = logic; } /*! \brief Gets the mass peak width calculation logic. \sa MsXpS::libXpertMassCore::Enums::MassPeakWidthLogic */ Enums::MassPeakWidthLogic MassPeakShaperConfig::getMassPeakWidthLogic() const { return m_massPeakWidthLogic; } double MassPeakShaperConfig::c(bool *ok) { // c in the Gaussian curve is related to the fwhm value: fwhm(ok); if(!*ok) { return 0; } double c = m_fwhm / (2 * sqrt(2 * log(2))); // qDebug() << "c:" << c; *ok = true; return c; } double MassPeakShaperConfig::a(bool *ok) { // double pi = 3.1415926535897932384626433832795029; double c_value = c(ok); if(!*ok) { return 0; } double a = (1 / (c_value * sqrt(2 * M_PI))); // qDebug() << "a:" << a; *ok = true; return a; } double MassPeakShaperConfig::gamma(bool *ok) { fwhm(ok); if(!*ok) { return 0; } double gamma = m_fwhm / 2; // qDebug() << "gamma:" << gamma; *ok = true; return gamma; } /*! \brief Sets the \a mz_step. The m/z step value is the distance between two consecutive points in the peak shape. \sa getMzStep() */ void MassPeakShaperConfig::setMzStep(double mz_step) { m_mzStep = mz_step; } /*! \brief Gets the m/z step. The m/z step value is the distance between two consecutive points in the peak shape. \sa setMzStep() */ double MassPeakShaperConfig::getMzStep() const { return m_mzStep; } /*! \brief Calculates the m/z step. The m/z step value is the distance between two consecutive points in the peak shape. To compute that value, the full m/z width of the calculated shape (not only the width at half maximum, FWHM) is divided by the total number of data points that make the final shape of the m/z peak. The first variable is FWHM_PEAK_SPAN_FACTOR * m_fwhm, and the second variable is m_pointCount. The calculation is thus: \code m_mzStep = (FWHM_PEAK_SPAN_FACTOR * m_fwhm) / m_pointCount; \endcode The first step is to compute the FWHM and then to check the value of m_pointCount (that cannot be 0). If any of these fails, the function returns 0 and set \a ok to false, otherwise the computed value is set to m_mzStep and returned, while \a ok is set to true. \sa FWHM_PEAK_SPAN_FACTOR */ double MassPeakShaperConfig::mzStep(bool *ok) { // But what is the mz step ? // // We want the shape to be able to go down to baseline. Thus we want that // the shape to have a "basis" (or, better, a "ground") corresponding to // twice the FWHM on the left of the centroid and to twice the FWHM on the // right (that makes in total FWHM_PEAK_SPAN_FACTOR * FWHM, that is, // FWHM_PEAK_SPAN_FACTOR = 4). fwhm(ok); if(!*ok) { return 0; } if(!m_pointCount) { *ok = false; return 0; } m_mzStep = (FWHM_PEAK_SPAN_FACTOR * m_fwhm) / m_pointCount; return m_mzStep; } /*! \brief Resets this MassPeakShaperConfig instance to default values like at construction time. */ void MassPeakShaperConfig::reset() { // Values to start over. m_resolution = 0; m_fwhm = 0; m_referencePeakMz = 0; m_pointCount = 0; m_withBins = false; m_binSizeDivisor = 6; m_binSize = 0; m_isBinSizeFixed = false; m_mzStep = 0; m_massPeakShapeType = Enums::MassPeakShapeType::NOT_SET; } /*! \brief Calculates the various parameters of the peak shape using member data. This function first checks that all the required data are set. Then this functions computes the FWHM value required to shape the peak centroid. Once the basic data have been computed or set, the parameters of the shape are computed: c, a, gamma. Returns true if the computation was successful, false otherwise. */ bool MassPeakShaperConfig::resolve(libXpertMassCore::ErrorList &error_list) { // We need to try to set all the relevant parameters by calculation. bool ok = false; // These are the essential parameters: if(!m_referencePeakMz) { error_list.append("Missing reference peak m/z value"); return false; } if(!m_fwhm && !m_resolution) { error_list.append("Both FWHM and resolution are naught"); return false; } if(!m_pointCount) { error_list.append("The count of points to shape the peak is naught"); return false; } // At this point we should be able to compute the relevant data. // The FWHM is the *leading* value for the determination of the peak shape's // width at half maximum. If that FWHM value is 0, then we resort to the // resolution. Both R and FWHM cannot be 0! if(m_fwhm) { // If we have FWHM, immediately try to compute the resolution as we'll // need it later. FWHM takes precedence over resolution! resolution(&ok); if(!ok) { error_list.append("Failed to compute resolution value"); return false; } } else { // We should be able to compute FWHM by resorting to the resolution. fwhm(&ok); if(!ok) { error_list.append("Failed to compute FWHM value"); return false; } } // Now check if we have and can compute the bins. // But we do this only if the user has not stated that the bin size has to // be set manually. // qDebug() << "In the resolve, check the bin size"; binSize(&ok); if(!ok) { error_list.append("Failed to compute the bin size"); return false; } mzStep(&ok); if(!ok) { error_list.append("Failed to compute the m/z step"); return false; } // Now the other parameters for the shape. c(&ok); if(!ok) { error_list.append("Failed to compute the c parameter"); return false; } a(&ok); if(!ok) { error_list.append("Failed to compute the a parameter"); return false; } gamma(&ok); if(!ok) { error_list.append("Failed to compute the gamma parameter"); return false; } return true; } /*! \brief Returns a string representing this MassPeakShaperConfig instance. The configuration of the MassPeakShaper is first resolved. If that step fails, an empty string is returned, otherwise the configuration is returned as a string. */ QString MassPeakShaperConfig::toString() { QString string; libXpertMassCore::ErrorList error_list; bool ok = resolve(error_list); if(!ok) return QString(); QString peak_shape_text; if(m_massPeakShapeType == Enums::MassPeakShapeType::GAUSSIAN) peak_shape_text = "Gaussian"; if(m_massPeakShapeType == Enums::MassPeakShapeType::LORENTZIAN) peak_shape_text = "Lorentzian"; QString with_bins_text; if(m_withBins) with_bins_text += QString("With bins of size: %1 m/z.\n").arg(m_binSize, 0, 'f', 10); else with_bins_text = "Without bins.\n"; string = QString( "%1 peak shaping:\n" "Configuration for reference m/z value: %2\n" "Resolution: %3\n" "FWHM: %4\n" "%5\n" "Number of points to shape the peak: %6\n" "c: %7\n" "c^2: %8\n" "mz step: %9\n\n") .arg(peak_shape_text) .arg(m_referencePeakMz, 0, 'f', 5) .arg(m_resolution) .arg(m_fwhm, 0, 'f', 5) .arg(with_bins_text) .arg(m_pointCount) .arg(c(&ok), 0, 'f', 5) .arg(c(&ok) * c(&ok), 0, 'f', 5) .arg(m_mzStep, 0, 'f', 5); return string; } void MassPeakShaperConfig::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register MassPeakShaperConfig class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&MassPeakShaperConfig::staticMetaObject); engine->globalObject().setProperty("MassPeakShaperConfig", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Modif.cpp000664 001750 001750 00000116735 15100504560 024042 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" int modifMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::Modif"); int modifPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::ModifPtr"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Modif \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Modif.hpp \brief The Modif class provides abstractions to work with chemical modifications. The Modif class provides a chemical modification that can be set to any monomer in a polymer sequence or to any one of the polymer sequence ends. In the protein world, chemical modifications of proteins that occur in the living cell are called post-translational modifications.This class aims at modelling, among others, such modifications. The chemical reaction described by the Modif class is encoded as an action-formula (see \l{Formula}). */ /*! \variable MsXpS::libXpertMassCore::Modif::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition. This member allows to root the Modif instance in a chemical context where IsotopicData can be used to calculate masses starting from a Formula, but also where the Monomer instances can be checked for existence when validating the Modif targets, for example. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_name \brief Name of the chemical modification, like "Acetylation". */ /*! \variable MsXpS::libXpertMassCore::Modif::m_formula \brief \l Formula string representing the chemical modification. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_targets \brief String that holds a list of all the target monomers of this modification. If there are more than one target, the targets (Monomer codes) must be separated by ';' characters. If any monomer in the polymer chemistry definition might be modified by this Modif object, then, the "*" string can be used to indicate so. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_maxCount \brief Value indicating the maximum number of times this modification can be set to a target entity (monomer or polymer). The value cannot be less than one, otherwise the Modif is set to an invalid state. This member is designed to prohibit modifying more than chemically possible a given Monomer. For example it is not possible to phosphorylate a Seryl residue more than once. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_mono \brief Value representing the monoisotopic mass of the formula. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_avg \brief Value representing the average mass of the formula. */ /*! \variable MsXpS::libXpertMassCore::Modif::m_isValid \brief Validity status of the Modif instance. */ /*! \brief Constructs a modification starting from an XML \a element according to \a version and using the reference polymer chemistry definition \a pol_chem_def_csp. The \a version indicates what version of the XML element is to be used. This is the current format: \code Acetylation C2H2O1 ;K; 1 AmidationAsp H1N1-O1 ;D; 1 \endcode The XML element is rendered using the dedicated function renderXmlMdfElement(). \sa renderXmlMdfElement() */ Modif::Modif(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, [[maybe_unused]] int version) : mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing Modif with no PolChemDef."; if(!renderXmlMdfElement(element, version)) { qCritical() << "Failed to fully render or validate the Modif XML element " "for construction of Modif instance."; } } /*! \brief Constructs a modification. A Modif instance cannot be of any use if it is not associated logically to a polymer chemistry definition (\a pol_chem_def_csp). The Modif instance is defined by its \a modif_name and its \a formula_string as as string (that defaults to empty in this constructor). Being able to construct a Modif without a formula string is necessary when Modif objects are intialized piecemeal upon reading XML elements that describe the Modif. The \a formula_string might be a simple formula ("O", for an oxidation) or an action-formula, if that is desirable to best characterize the modification ("-H20+CH3COOH", for example, for an acetylation). The formula string can also have a title, like: \c{"Acetylation"-H20+CH3COOH}. The member string datum representing the allowed targets of this Modif instanceis set to "all" (that is, \c{"*"}) and the maximum count that this modification can bet set to a given target is set to \c{1} by default, like a Seryl residue can only be phosphorylated once, for example. After setting the member data and if the polymer chemistry definition is available, \l validate() is called, the masses are calculated and if all went without error the member \l m_isValid validity status is set to true, otherwise it is set to false. */ Modif::Modif(PolChemDefCstSPtr pol_chem_def_csp, const QString &modif_name, const QString &formula_string, double mono, double avg) : mcsp_polChemDef(pol_chem_def_csp), m_name(modif_name), m_formula(formula_string), m_mono(mono), m_avg(avg) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Construction of Modif with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } // Validation tries to compute masses but does not update member data. // Here we want to update the member data. if(!calculateMasses(nullptr)) { m_isValid = false; qCritical() << "Construction of Modif with masses that failed to be " "calculated."; } } /*! \brief Constructs a Modif object as a copy of \a other. After setting the member data and if the polymer chemistry definition is available, \l validate() is called, the masses are calculated and if all went without error the member \l m_isValid validity status is set to true, otherwise it is set to false. */ Modif::Modif(const Modif &other) : PropListHolder(other), mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_formula(other.m_formula), m_targets(other.m_targets), m_maxCount(other.m_maxCount), m_mono(other.m_mono), m_avg(other.m_avg) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Construction of Modif with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } // Validation tries to compute masses but does not update member data. // Here we want to update the member data. if(!calculateMasses(nullptr)) { m_isValid = false; qCritical() << "Construction of Modif with masses that failed to be " "calculated."; } } /*! \brief Destructs this Modif. */ Modif::~Modif() { } /*! \brief Sets the polymer chemistry definition to \a pol_chem_def_csp. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Modif::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Modif with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the polymer chemistry definition. */ const PolChemDefCstSPtr & Modif::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the \a name. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Modif::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Modif with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ QString Modif::getName() const { return m_name; } /*! \brief Sets the formula to \a formula_string. After setting the member data and if the polymer chemistry definition is available, \l validate() is called, the masses are calculated and if all went without error the member \l m_isValid validity status is set to true, otherwise it is set to false. */ void Modif::setFormula(const QString &formula_string) { m_formula = formula_string; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Failed to validate the Modif with errors:\n" << Utils::joinErrorList(error_list, ", "); } // Validation tries to compute masses but does not update member data. // Here we want to update the member data. if(!calculateMasses(nullptr)) { m_isValid = false; qCritical() << "Failed to calculate the masses after setting formula to Modif."; } } /* \brief Returns the formula describing this modification. */ const QString & Modif::getFormula() const { return m_formula; } /* \brief Returns a copy of the member formula describing this modification. If \a with_title is true, the title of the formula prepended to the formula string. For example, with title, formula would be \c{"Acetylation"-H2O+CH3COOH}. */ QString Modif::formula(bool with_title) const { Formula temp_formula(m_formula); return temp_formula.getActionFormula(with_title); } /*! \brief Sets the \a targets for this modification. Setting the targets of a Modif instance means specifying which target (Monomer code) might be modified using this modification. For example, for \c Phosphorylation, in protein chemistry, one would define targets as \c Serine, \c Threonine, \c Tyrosine (there are other targets, like Histidine, but very rarely encountered). Multiple targets are separated using ';'. The \a targets are validated (\l Modif::validateTargets()) and the obtained string is set to m_targets. If the validation fails, m_targets is set to QString(). After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Modif::setTargets(const QString &targets) { qDebug() << "Validating targets:" << targets; QString local_targets_string = targets; local_targets_string = Utils::unspacify(local_targets_string); qDebug() << "After unspacification" << local_targets_string; bool ok = false; // Validate without simplification (false). local_targets_string = validateTargets(local_targets_string, /* simplification */ false, ok); if(!ok) { qCritical() << "The validation of the Modif targets failed:" << local_targets_string; m_targets = QString(); } else m_targets = local_targets_string; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Modif with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the tagets of this modification. */ QString Modif::getTargets() const { return m_targets; } /*! \brief Returns true if Mnomer \a code is found among the targets of this Modif instance, false otherwise. */ bool Modif::doesTargetMonomer(const QString &code) const { if(m_targets == "*") return true; if(m_targets == "!") return false; QString delimitedCode = QString(";%1;").arg(code); // The m_targets string is in the form ";code;code;code;". // qDebug() << "The targets:" << m_targets; return m_targets.contains(delimitedCode, Qt::CaseSensitive); } /*! \brief Sets the maximum count (times the modification event is allowed to occur on a target) that this modification might be set to a target to \a value. For Phosphorylation, for example, that would be \c{1} count exclusively for modification of Seryl, Threonyl and Tyrosinyl residues. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ void Modif::setMaxCount(int value) { m_maxCount = value; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Modif with errors:" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the maximum count of times that this modification might be set to a target. */ int Modif::getMaxCount() const { return m_maxCount; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this modification. Returns a reference to this modification. After setting the member data, \l validate() is called and the \l m_isValid member is set to the result of the validation. */ Modif & Modif::operator=(const Modif &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_formula = other.m_formula; m_targets = other.m_targets; m_maxCount = other.m_maxCount; m_mono = other.m_mono; m_avg = other.m_avg; PropListHolder::operator=(other); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Assignment of Modif with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } return *this; } /*! \brief Returns true if this and the \a other modifications are identical, false otherwise. */ bool Modif::operator==(const Modif &other) const { if(&other == this) return true; // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). return m_name == other.m_name && m_formula == other.m_formula && m_targets == other.m_targets && m_maxCount == other.m_maxCount && m_mono == other.m_mono && m_avg == other.m_avg; } /*! \brief Returns true if this and the \a other modifications differ, false otherwise. Returns the negated result of operator==(). */ bool Modif::operator!=(const Modif &other) const { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns the Modif instance from the polymer chemistry definition registered in this instance. The key to search the Modif is this instance's member name. If there is no PolChemDef available, nullptr is returned. If no Modif instance is found by this instance's name, nullptr is returned. */ ModifCstSPtr Modif::getFromPolChemDefByName() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_name.isEmpty()) return nullptr; return mcsp_polChemDef->getModifCstSPtrByName(m_name); } /*! \brief Returns the status of this Modif instance the polymer chemistry definition registered in this instance. The key to search the Modif is this instance's member name. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no Modif instance is found by this instance's name, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus Modif::isKnownByNameInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getModifCstSPtrByName(m_name) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; }; /*! \brief Validates the \a targets_string target and returns the processed targets string. If \a targets_string is non-empty, it is validated. Otherwise, \a ok is set to false and \a targets_string is returned unchanged. If \a simplify is true, then, whenever '*' or '!' is encountered in \a targets_string, then \a ok is set to true and either '*' or '!' is returned. Indeed, the target list is split using ';' as a delimiter. If '*' is found and \a simplify is true, the function returns '*' immediately (as all the monomers in the polymer chemistry definition might be a target of this modification) and \a ok is set to true. Likewise, if '!' is found and \a simplify is true, the function returns '!' immediately (as none of all the monomers in the polymer chemistry definition might be a target of this modification) and \a ok is set to true. If at least one target is found, then each target is a monomer code and that code is looked for in the member polymer chemistry definition list of monomers. If the code is found, that code is added to a temporary string. If the code is not found, \a ok is set to false and \a targets_string is returned unchanged. When the process completes, without error, \a ok is set to true and the string corresponding to the processed monomer codes in the \a targets_string is returned. Otherwise, \a ok is set to false and an empty string is returned. */ QString Modif::validateTargets(const QString &targets_string, bool simplify, bool &ok) const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "Cannot validate the targets because no PolChemDef is defined."; m_isValid = false; return QString(); } QString local_targets_string = targets_string; // If the local_targets_string is empty, this is an error, because we cannot // know what's to be done with the modification. if(local_targets_string.isEmpty()) { ok = false; return QString(); } // A targets string cannot contain both a '*' and a '!' // character. We check that immediately. if(local_targets_string.contains('*') && local_targets_string.contains('!')) { qWarning() << "A modification targets string cannot contain both '*' and '!'."; ok = false; return QString(); } QString processed_targets_string; // A targets string looks like "Ser ; Thr ; Tyr". QStringList code_string_list = local_targets_string.split(';', Qt::SkipEmptyParts, Qt::CaseSensitive); for(int iter = 0; iter < code_string_list.size(); ++iter) { QString iter_code_string = code_string_list.at(iter); // There are two character that might be encountered: '*' is the // equivalent of "all the monomers in the definition"; '!' is // equivalent to "none of the monomers in the definition". But // it is not possible that both * and ! be present in the same // targets string. if(iter_code_string == "*" || iter_code_string == "!") { // Simplification asked: if '*' is found then it can be // there alone. Same for '!'. '*' means that any monomer in // the definition might be modified with this modification, // '!' means that none of the monomers might be modified. if(simplify) { // qDebug() << "Simplification asked for."; processed_targets_string = iter_code_string; break; } else { processed_targets_string.append( QString("%1;").arg(iter_code_string)); } continue; } // At this point, we have something to check as a monomer code: if(mcsp_polChemDef->getMonomerCstSPtrByCode(iter_code_string) != nullptr) { // Want the string to be ;code;code;code;(notice the first // and last ';'), so that we can later ask easily if ;Lys; // is found in the targets string, without risking to also // match ;Ly;. if(!processed_targets_string.size()) processed_targets_string.append( QString(";%1;").arg(iter_code_string)); else processed_targets_string.append( QString("%1;").arg(iter_code_string)); } else { qDebug() << "Monomer code is not known:" << iter_code_string; ok = false; return QString(); ; } } // End of // for(int iter = 0; iter < code_string_list.size(); ++iter) // qDebug() << "processed_targets_string:" << processed_targets_string; if(processed_targets_string.isEmpty()) { qDebug() << "After the validation process, the targets string results empty."; ok = false; return QString(); ; } // Most evident fix to the loop output above if(processed_targets_string == "*;") processed_targets_string = "*"; if(processed_targets_string == "!;") processed_targets_string = "!"; ok = true; return processed_targets_string; } /*! \brief Validates the member targets and returns the processed targets string. If \a simplify is true, then, whenever '*' or '!' is encountered in the member targets string, then \a ok is set to true and either '*' or '!' is returned. Indeed, the target list is split using ';' as a delimiter. If '*' is found and \a simplify is true, the function returns '*' immediately (as all the monomers in the polymer chemistry definition might be a target of this modification) and \a ok is set to true. Likewise, if '!' is found and \a simplify is true, the function returns '!' immediately (as none of all the monomers in the polymer chemistry definition might be a target of this modification) and \a ok is set to true. If at least one target is found, then each target is a monomer code and that code is looked for in the member polymer chemistry definition list of monomers. If the code is found, that code is added to a temporary string. If the code is not found, \a ok is set to false and the member targets string is returned unchanged. When the process completes, without error, \a ok is set to true and the string corresponding to the processed monomer codes in the member targets string is returned. Otherwise, \a ok is set to false and an empty string is returned. */ QString Modif::validateTargets(bool simplify, bool &ok) { return validateTargets(m_targets, simplify, ok); } /*! \brief Validates this modification, sets the validity status accordingly and returns it. Any potential error is reported with a message added to \a error_list_p (that is not cleared). The modification validates successfully if: \list \li The member polymer chemistry definition is available and the isotopic data inside it also \li The name is not empty \li The formula validates successfully \li The masses can be calculated (on local mass variables, given constness of this function) \li The targets validate successfully (that is, they reference Monomer codes known to the polymer chemistry definition) \li The m_maxCount member is greater than 0 \endlist If the validation is successful, the validity status (m_isValide) is set to true, to false otherwise, and returned. */ bool Modif::validate(ErrorList *error_list_p) const { Q_ASSERT(error_list_p != nullptr); qsizetype error_count = error_list_p->size(); m_isValid = false; if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr || !mcsp_polChemDef->getIsotopicDataCstSPtr()->size()) { error_list_p->push_back( "The PolChemDef or the IsotopicData are not available"); qCritical() << "The polymer chemistry definition member datum is nullptr or " "its isotopic data are be either nullptr or empty. Modif validation " "failed."; return false; } if(m_name.isEmpty()) { qCritical() << "The Modif name is empty."; error_list_p->push_back("The Modif name is empty"); } Formula temp_formula(m_formula); if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "The Modif formula failed to validate."; error_list_p->push_back("The Modif formula failed to validate"); } double mono = 0.0; double avg = 0.0; if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg)) { qCritical() << "Failed to calculate the Modif masses."; error_list_p->push_back("Failed to calculate the Modif masses"); } bool ok = false; // Validate without simplification (false). QString targets_after_validation = validateTargets(m_targets, /* simplification */ false, ok); if(!ok) { qCritical() << "The Modif targets failed to validate."; error_list_p->push_back("The Modif targets failed to validate"); } if(m_maxCount <= 0) { qCritical() << "The maximum modification count failed to validate."; error_list_p->push_back("The maximum modification count failed to validate"); } m_isValid = (error_list_p->size() > error_count ? false : true); return m_isValid; } /*! \brief Returns the validity status of this Modif instance. */ bool Modif::isValid() const { return m_isValid; } //////////////// MASS CALCULATIONS ///////////////////// /*! \brief Calculates the net masses of this modification and sets the results in \a mono and \a avg. The masses of the modification are the masses (monoisotopic and average) that are added to the target as a result of that target being modified with this modification. The mass calculations are performed using reference data in \a isotopic_data_csp. If \a isotopic_data_csp is nullptr, then the reference data are searched in the member polymer chemistry definition. Returns true if the mass calculations were successful, false otherwise. If the calculations failed, m_isValid is set to false. \sa Formula::accountMasses() */ bool Modif::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg) const { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; m_isValid = false; return false; } } // qDebug() << "Calculating masses for" << m_name; mono = 0; avg = 0; bool ok; // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg); if(!ok) { qCritical() << "Failed accounting masses for Modif:" << m_name << "and formula:" << m_formula; m_isValid = false; } return ok; } /*! \brief Calculates the net masses of this modification. The masses of the modification are the masses (monoisotopic and average) that are added to the target as a result of that target being modified with this modification. The calculated masses are set to the m_mono and m_avg members. The mass calculations are performed using reference data in \a isotopic_data_csp. If \a isotopic_data_csp is nullptr, then the reference data are searched in the member polymer chemistry definition. Returns true if the mass calculations were successful, false otherwise. If the calculations failed, m_isValid is set to false. \sa Formula::accountMasses() */ bool Modif::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp) { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; return false; } } // qDebug() << "Calculating masses for" << m_name; m_mono = 0; m_avg = 0; bool ok; // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, m_mono, m_avg); if(!ok) { qCritical() << "Failed accounting masses for Modif:" << m_name << "and formula:" << m_formula; m_isValid = false; } return ok; } /*! \brief Calculates the net masses of this modification. The masses of the modification are the masses (monoisotopic and average) that are added to the target as a result of that target being modified with this modification. The calculated masses are set to the m_mono and m_avg members. The mass calculations are performed using reference data in \a isotopic_data_csp. If \a isotopic_data_csp is nullptr, then the reference data are searched in the member polymer chemistry definition. Returns a reference to this Modif instance. If the calculations failed, m_isValid is set to false. \sa Formula::accountMasses() */ Modif & Modif::calculateMasses(bool &ok, const IsotopicDataCstSPtr &isotopic_data_csp) { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; ok = false; return *this; } } // qDebug() << "Calculating masses for" << m_name; m_mono = 0; m_avg = 0; // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, m_mono, m_avg); if(!ok) { qCritical() << "Failed accounting masses for Modif:" << m_name << "and formula:" << m_formula; m_isValid = false; } return *this; } /*! \brief Adds to \a mono_p and \a avg_p the corresponding mass of this modification. The m_mono and m_avg masses are added to the arguments. The masses are compounded by factor \a times before the addition. Returns this object. */ const Modif & Modif::accountMasses(double *mono_p, double *avg_p, int times) const { if(mono_p != nullptr) *mono_p += m_mono * times; if(avg_p != nullptr) *avg_p += m_avg * times; return *this; } /*! \brief Adds to \a mono and \a avg the corresponding mass of this modification. The m_mono and m_avg masses are added to the arguments. The masses are compounded by factor \a times before the addition. Returns this object. */ const Modif & Modif::accountMasses(double &mono, double &avg, int times) const { mono += m_mono * times; avg += m_avg * times; return *this; } /*! \brief Returns the mass of the type defined by \a mass_type. */ double Modif::getMass(Enums::MassType mass_type) { if(mass_type == Enums::MassType::MONO) return m_mono; else if(mass_type == Enums::MassType::AVG) return m_avg; else qFatalStream() << "Not possible to ask a mass that is not MONO nor AVG"; return -1; } /*! \brief Parses the modification XML \a element specifically for \a version. Parses the modif \c mdf XML element passed as argument and for each encountered data will set the data to this modif (this is called XML rendering).The parsing is delegated to a function that is specific for \a version of the polymer chemistry definition. The \c mdf XML element is found in the polymer chemistry definition and has the following form: \code Acetylation C2H2O1 ;K; 1 AmidationAsp H1N1-O1 ;D; 1 \endcode After setting all the data, this modification calculates it masses and validates itself. If any of these steps fails, the error is reported by returning false. Returns true if parsing was successful, false otherwise. */ bool Modif::renderXmlMdfElement(const QDomElement &element, [[maybe_unused]] int version) { // Assume this, we later negate this as we go. m_isValid = true; // QDomDocument doc; // QDomElement root = doc.importNode(element, true).toElement(); // doc.appendChild(root); // qDebug() << "The element:" << doc.toString(); if(element.tagName() != "mdf") { qCritical() << "The element tag name is not 'mdf'"; m_isValid = false; return m_isValid; } QDomElement child; child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The Modif did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); // qDebug() << "The name:" << m_name; child = child.nextSiblingElement("formula"); if(child.isNull()) { qCritical() << "The Modif did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } Formula temp_formula(nullptr); if(!temp_formula.renderXmlFormulaElement(child)) { qCritical() << "The Modif did not render correctly: the formula did not " "render correctly."; m_isValid = false; return m_isValid; } m_formula = temp_formula.getActionFormula(/*with_title*/ true); // qDebug() << "The formula:" << m_formula; child = child.nextSiblingElement("targets"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The Modif did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_targets = child.text(); bool ok = false; QString targets_after_validation = validateTargets(m_targets, /* simplify */ false, ok); // qDebug() << "The targets after validation:" << targets_after_validation; if(!ok) { qCritical() << "The Modif did not render correctly: the targets could " "not validate successfully."; m_isValid = false; return m_isValid; } m_targets = targets_after_validation; child = child.nextSiblingElement("maxcount"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The Modif did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } QString max_count_string = child.text(); ok = false; int max_count_int = max_count_string.toInt(&ok); if(!ok || max_count_int <= 0) { qCritical() << "The Modif did not render correctly: the maxcount value " "is less than 0."; m_isValid = false; return m_isValid; } m_maxCount = max_count_int; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Modif did not validate successfully after " "rendering, with errors:"; Utils::joinErrorList(error_list, ", "); } else { // At this point, because we are creating a Modif from scratch, // and not by copying or by assignment, we calculate the masses // explicitely (validate() does that but not on member m_mono/m_avg // because the method is const.). if(!calculateMasses(nullptr)) { qCritical() << "The Modif's masses could not be calculated."; } } qDebug() << "Correctly rendered Modif" << m_name; return m_isValid; } /*! \brief Formats this modification's data as a string suitable to be used as a \c mdf XML element in the polymer chemistry definition or a polymer sequence file. The typical modification element that is generated in this function looks like this: \code Acetylation C2H2O1 ;K; 1 AmidationAsp H1N1-O1 ;D; 1 \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a string. */ QString Modif::formatXmlMdfElement(int offset, const QString &indent) const { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: Phosphorylation -H+H2PO3 S;T;Y 1 */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n").arg(lead).arg(m_formula); text += QString("%1%2\n").arg(lead).arg(m_targets); text += QString("%1%2\n").arg(lead).arg(m_maxCount); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); // QString debug_message = // QString("%1\n%2\n").arg("Returning string:").arg(text); // qCritical().noquote() << debug_message; return text; } /*! \brief Resets this modification to an empty object. */ void Modif::clear() { m_name.clear(); m_formula.clear(); m_targets.clear(); m_maxCount = -1; m_mono = 0; m_avg = 0; m_isValid = false; } /*! \brief Returns a string representing this Modif instance. */ QString Modif::toString() const { return QString("%1, %2, %3, %4") .arg(m_name) .arg(m_formula) .arg(m_targets) .arg(m_maxCount); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Monomer.cpp000664 001750 001750 00000155103 15100504560 024410 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes #include /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" int monomerMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::Monomer"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Monomer \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Monomer.hpp \brief The Monomer class provides abstractions to work with monomers. In libmass, a momomer is an entity that is part of a polymer chemistry definition. A monomer models a chemical entity that is part of a polymer. In protein chemistry, that would be a \e{residue}, that is, an amino-acid that has been polymerized into a residue chain (that is, a protein, or a peptide). The chemical reaction that polymerizez an amino acid into an elongating protein structure is a condensation, with loss of H2O from the amino acid to actually lead to a what is called a \e{residue} of a monomer, or for short a \e{residue}. \note The monomer, that is partly defined by its formula, has the formula of the \e{residue}, not of the amino acid. This is always true, whatever the polymer chemistry definition at hand: protein, saccharide, nucleic acid. */ /*! \variable MsXpS::libXpertMassCore::Monomer::mcsp_polChemDef \brief The polymer chemistry definition that is the context in which the Monomer instance exists. */ /*! \variable MsXpS::libXpertMassCore::Monomer::m_name \brief The name of the monomer, like Lysine, Adenine. */ /*! \variable MsXpS::libXpertMassCore::Monomer::m_code \brief The code of the monomer, like K for lysine, A for adenine. */ /*! \variable MsXpS::libXpertMassCore::Monomer::m_formula \brief The formula of the monomer. */ /*! \variable MsXpS::libXpertMassCore::Monomer::m_modifs \brief The container of \l Modif instances that are involved in the modification of this Monomer. \note The Modif pointers stored in the m_modifs member are ModifSPtr pointers. */ /*! \variable MsXpS::libXpertMassCore::Monomer::m_isValid \brief The validity status of the Monomer. */ /*! \typedef MsXpS::libXpertMassCore::MonomerRPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for Monomer *. */ /*! \typedef MsXpS::libXpertMassCore::MonomerCstRPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for const Monomer *. */ /*! \typedef MsXpS::libXpertMassCore::MonomerUPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::unique_ptr. */ /*! \typedef MsXpS::libXpertMassCore::MonomerCstUPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::unique_ptr. */ /*! \typedef MsXpS::libXpertMassCore::MonomerSPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::MonomerCstSPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::MonomerWPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::weak_ptr. */ /*! \typedef MsXpS::libXpertMassCore::MonomerCstWPtr \relates MsXpS::libXpertMassCore::Monomer Synonym for std::weak_ptr. */ /*! \typealias MsXpS::libXpertMassCore::UuidMonomerCstWPtrPair \relates MsXpS::libXpertMassCore::Monomer Synonym for std::pair items. These pairs are used to store a unique identifier (Uuid) string related to a std::shared_ptr type. This kind of pair is used in a container in the \l CrossLink class. The fact that the std::shared_ptr is converted to a std::weak_ptr is interesting because the item in the pair will not increase the reference count. */ /*! \brief Constructs a monomer starting from an XML \a element according to \a version and using the reference polymer chemistry definition \a pol_chem_def_csp. This is the current format: \code Glycine G C2H3N1O1 \endcode */ Monomer::Monomer(PolChemDefCstSPtr pol_chem_def_csp, const QDomElement &element, int version) : mcsp_polChemDef(pol_chem_def_csp) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Constructing Monomer with no PolChemDef."; if(!renderXmlMnmElement(element, version)) qCritical() << "Failed to fully render or validate the Monomer XML element " "for construction of Monomer instance."; // We cannot ask for validation because the polymer chemistry definition // might be unavailable when using the XML element rendering. qDebug() << "Constructed Monomer" << m_name << ":" << toString(); } /*! \brief Constructs a monomer with its member data set to \a name, \a code, \a formula_string, The \a pol_chem_def_csp and both masses \a mono and \a avg. The member m_isValid validity status is set to the result of this Monomer validation. */ Monomer::Monomer(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &code, const QString &formula_string, double mono, double avg) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_code(code), m_formula(formula_string), m_mono(mono), m_avg(avg) { if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr) { ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Construction of Monomer with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } } else m_isValid = false; } /*! \brief Constructs a monomer as a copy of \a other. */ Monomer::Monomer(const Monomer &other) : PropListHolder(other), mcsp_polChemDef(other.mcsp_polChemDef), m_name(other.m_name), m_code(other.m_code), m_formula(other.m_formula), m_mono(other.m_mono), m_avg(other.m_avg) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Copy-constructing Monomer with no PolChemDef."; // We do a real duplication of the Modif instances. for(ModifSPtr modif_sp : other.m_modifs) storeModif(std::make_shared(*modif_sp)); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Copy-construction of Monomer with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Destroys the monomer. */ Monomer::~Monomer() { m_modifs.clear(); } //////////////// THE POLCHEMDEF ///////////////////// /*! \brief Sets the polymer chemistry definition to \a pol_chem_def_csp. */ void Monomer::setPolChemDefCstSPtr(PolChemDefCstSPtr &pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Monomer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the polymer chemistry definition. */ const PolChemDefCstSPtr & Monomer::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the \a name. */ void Monomer::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Monomer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ QString Monomer::getName() const { return m_name; } //////////////// THE CODE ///////////////////// /*! \brief Sets the code to \a code */ void Monomer::setCode(const QString &code) { m_code = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Monomer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the code */ QString Monomer::getCode() const { return m_code; } /*! \brief Checks the code's syntactic validity. If \a code_length is not -1, then that length is used for the check. Otherwise, the length from the polymer chemistry definition (if available) is used. The monomer code is verified and has to verify these criteria: \list \li It must be non-empty \li Its character length has to be less or equal to the code length parameter in the polymer chemistry definition (see PolChemDef::m_codeLength) \li The first character is uppercase \li All the remaining characters are lowercase \endlist Returns true if the code syntax checked successfully, false otherwise. \sa validate() */ bool Monomer::checkCodeSyntax(int code_length) const { int local_code_length = code_length; if(local_code_length == -1) { if(mcsp_polChemDef != nullptr || mcsp_polChemDef.get() != nullptr) { local_code_length = mcsp_polChemDef->getCodeLength(); } else { qCritical() << "Checking code syntax with unlimited length because " "Monomer has no PolChemDef."; local_code_length = 1000; } } qDebug() << "Checking code syntax:" << m_code << "with code length:" << local_code_length; // The code has to be at least one character long. // The first letter in the code has to be uppercase. // All the remaining authorized characters have to be // lowercase. if(m_code.length() < 1 || m_code.length() > local_code_length) { qCritical() << "The code has a length:" << m_code.length() << "that does not match the expected code length:" << local_code_length; m_isValid = false; return false; } // Note that the actual monomer code length might be less than the // codeLength member datum in the polymer chemistry // definition. QString regexp_string = QString("([A-Z])([a-z]{0,%1})").arg(local_code_length - 1); QRegularExpression regexp(regexp_string); QRegularExpressionMatch match = regexp.match(m_code); if(match.hasMatch()) { qDebug() << "First uppercase char:" << match.captured(1); qDebug() << "Remaining lowercase chars:" << match.captured(2); return true; } else { qDebug() << "Failed to RegExp match the monomer code:" << m_code << "against an expected code length of:" << local_code_length; } return false; #if 0 // Old version for(int iter = 0; iter < m_code.size(); ++iter) { // Test that the m_code length is not greater than codeLength. if(iter + 1 > codeLength) { PolChemDefEntity::m_isValid = false; return false; } // And now check the character syntax. QChar curChar = m_code.at(iter); if(iter == 0) { if(curChar.category() != QChar::Letter_Uppercase) { PolChemDefEntity::m_isValid = false; return false; } } else if(curChar.category() == QChar::Letter_Uppercase) { PolChemDefEntity::m_isValid = false; return false; } } return true; #endif } //////////////// THE FORMULA ///////////////////// /*! \brief Sets the formula to \a formula_string. */ void Monomer::setFormula(const QString &formula_string) { m_formula = formula_string; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Monomer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /* \brief Returns the formula of this Monomer. */ const QString & Monomer::getFormula() const { return m_formula; } //////////////// THE MODIFICATIONS ///////////////////// /*! \brief Returns the container of Modif instances that modify this Monomer. */ const std::vector & Monomer::getModifsCstRef() const { return m_modifs; } /*! \brief Returns the list of Modif names in the same order as the Modif instances are stored in the member container. */ std::vector Monomer::modifNamesInOrder() const { std::vector names; for(const ModifSPtr &modif_sp : m_modifs) names.push_back(modif_sp->getName()); return names; } /* \brief Returns true if this monomer is a target of Modif \a modif, false otherwise. */ bool Monomer::isModifTarget(const Modif &modif) const { // Pure convenience function. // qDebug() << "The modif:" << modif.toString(); return modif.doesTargetMonomer(m_code); } /*! \brief Modifies this monomer using \a modif. These two verifications that are done: \list \li This monomer must be a target of \a modif; \li The count of \a modif modifications in this monomer must be at most the authorized count - 1, to accomodate this new modification [see Modif::maxCount()]. \endlist The two restrictions above can be overridden by setting \a override to true. If errors are encountered, these are reported as strings in \a error_list_p. Returns a string with the Uuid of the allocated Modif instance. */ QString Monomer::modify(const Modif &modif, bool override, ErrorList *error_list_p) { if(!isModifTarget(modif) && !override) { // This monomer is not a target for the modif, and no override // is allowed. error_list_p->push_back( QString("%1 not target of Modif %2 (no overriding allowed)") .arg(m_name) .arg(modif.getName())); return QString(); } qDebug() << "Good, the monomer is target of this modif."; int count = countModifsByName(modif.getName()); qDebug() << "There are" << count << "modifs by that name."; if(count >= modif.getMaxCount() && !override) { // This monomer has already the maximum count of 'modif' objects. error_list_p->push_back( QString("%1 already modified %2 times (no overriding allowed)") .arg(m_name) .arg(count)); return QString(); } // We do a real allocation of a new Modif instance. qDebug() << "Now allocating ModifSPtr:" << modif.getName() << "and calling storeModifSPtrAsUuid with it."; ModifSPtr modif_sp = std::make_shared(modif); if(modif_sp == nullptr || modif_sp.get() == nullptr) qFatalStream() << "Failed to allocated new Modif object."; QString uuid = storeModif(modif_sp); qDebug() << "The stored Modif:" << uuid << getModifForUuid(uuid)->getName(); return uuid; } /*! \brief Modifies this monomer using \a modif_name. \a modif_name is used to find a Modif instance in the polymer chemistry definition. If such a Modif is found, it is used to modify this Monomer. Returns a string with the Uuid of the allocated Modif instance. */ QString Monomer::modify(const QString &modif_name, bool override, ErrorList *error_list_p) { ModifCstSPtr modif_csp = mcsp_polChemDef->getModifCstSPtrByName(modif_name); if(modif_csp == nullptr) { qCritical() << "Modif by name" << modif_name << "is not known."; return QString(); } return modify(*modif_csp, override, error_list_p); } /*! \brief Removes from this monomer the Modif instance tagged using the Uuid \a uuid string. Returns true if the unmodification was actually performed, false otherwise. */ bool Monomer::unmodify(const QString &uuid) { std::vector::iterator the_iterator = std::find_if( m_uuidModifPairs.begin(), m_uuidModifPairs.end(), [uuid](UuidModifWPtrPair &pair) { return pair.first == uuid; }); if(the_iterator == m_uuidModifPairs.end()) { qCritical() << "The modification was not found."; return false; } // Sanity check if((*the_iterator).second.expired() || (*the_iterator).second.lock() == nullptr || !hasModif((*the_iterator).second.lock())) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; if(!removeModif((*the_iterator).second.lock())) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; return true; } /*! \brief Removes \e{all} the modification from this monomer. Returns true if at least one Modif instance was removed, false if this Monomer instance was not modified. */ bool Monomer::unmodify() { std::size_t modif_count = m_modifs.size(); // Sanity check if(m_modifs.size() != m_uuidModifPairs.size()) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; m_modifs.clear(); m_uuidModifPairs.clear(); return modif_count != 0; } /*! \brief Returns true if this monomer has at least one modification, false otherwise. */ bool Monomer::isModified() const { return m_modifs.size(); } /*! \brief Returns the count of modifications by name \a modif_name in this monomer. */ int Monomer::countModifsByName(const QString &modif_name) { int count = std::count_if( m_modifs.begin(), m_modifs.end(), [modif_name](const ModifSPtr &modif_sp) { return modif_sp->getName() == modif_name; }); return count; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other's member data to this monomer. The copy is deep, in particular with the mpa_modifList being copied. Returns a reference to this monomer. */ Monomer & Monomer::operator=(const Monomer &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_name = other.m_name; m_code = other.m_code; m_formula = other.m_formula; m_mono = other.m_mono; m_avg = other.m_avg; // We want the modifs to be stored anew. So first clear. m_modifs.clear(); // We do a real duplication of the Modif instances. for(const ModifSPtr &modif_sp : other.m_modifs) storeModif(std::make_shared(*modif_sp)); PropListHolder::operator=(other); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "Assignment of Monomer with validation errors:\n" << Utils::joinErrorList(error_list, ", "); } // qDebug() << "Assignment of Monomer produced *this Monomer:" << toString(); return *this; } /*! \brief Returns true if this monomer and \a other are identical, false otherwise. The comparison involves also the comparison of the Modif objects in mpa_modifList. */ bool Monomer::operator==(const Monomer &other) const { if(&other == this) return true; // We cannot compare the PolChemDef, because that would cause // an infinite loop: (each instance of this class in the PolChemDef would // try to compare the PolChemDef...). if(m_name != other.m_name || m_code != other.m_code || m_formula != other.m_formula || m_mono != other.m_mono || m_avg != other.m_avg) return false; if(m_modifs.size() != other.m_modifs.size()) { // qDebug() << "The Modif containers have different sizes."; return false; } // We do a deep Modif instance comparison. for(std::size_t iter = 0; iter < m_modifs.size(); ++iter) { if(*m_modifs.at(iter) != *other.m_modifs.at(iter)) { // qDebug() << "At least one Modif instance differs in both Monomer // instances."; return false; } } return true; } /*! \brief Returns true if \c this monomer and \a other differ, false otherwise. Returns the negated result of operator==(other). */ bool Monomer::operator!=(const Monomer &other) const { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Returns the Monomer instance from the polymer chemistry definition registered in this instance. The key to search the Monomer is this instance's member name. If there is no PolChemDef available, nullptr is returned. If no Monomer instance is found by this instance's name, nullptr is returned. */ MonomerSPtr Monomer::getFromPolChemDefByName() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_name.isEmpty()) return nullptr; return mcsp_polChemDef->getMonomerCstSPtrByName(m_name); } /*! \brief Returns the Monomer instance from the polymer chemistry definition registered in this instance. The key to search the Monomer is this instance's member code. If there is no PolChemDef available, nullptr is returned. If no Monomer instance is found by this instance's code, nullptr is returned. */ MonomerSPtr Monomer::getFromPolChemDefByCode() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return nullptr; if(m_code.isEmpty()) return nullptr; return mcsp_polChemDef->getMonomerCstSPtrByCode(m_code); } /*! \brief Returns the status of this Monomer instance the polymer chemistry definition registered in this instance. The key to search the Monomer is this instance's member name. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no Monomer instance is found by this instance's name, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus Monomer::isKnownByNameInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getMonomerCstSPtrByName(m_name) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; } /*! \brief Returns the status of this Monomer instance the polymer chemistry definition registered in this instance. The key to search the Monomer is this instance's member code. If there is no PolChemDef available, Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned. If no Monomer instance is found by this instance's code, Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned. */ Enums::PolChemDefEntityStatus Monomer::isKnownByCodeInPolChemDef() const { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE; if(mcsp_polChemDef->getMonomerCstSPtrByCode(m_code) != nullptr) return Enums::PolChemDefEntityStatus::ENTITY_KNOWN; return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; } /*! \brief Returns true if this monomer is valid, false otherwise. Validation of the monomer occurs if: \list \li Its name is not empty \li Its code is not empty and its syntax is correct \li Its formula validates \li Its modifications (if any) validate \endlist If errors are encountered, describing message are stored in \a error_list_p. \sa checkCodeSyntax() */ bool Monomer::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr || mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr || !mcsp_polChemDef->getIsotopicDataCstSPtr()->size()) { error_list_p->push_back("The PolChemDef or IsotopicData are not available"); qCritical() << "The polymer chemistry definition member datum is nullptr or " "its isotopic data are be either nullptr or empty. Monomer " "validation " "failed."; m_isValid = false; return false; } if(m_name.isEmpty()) { qCritical() << "The Monomer name is empty."; error_list_p->push_back("The Monomer name is empty"); } // qDebug() << "Now checking the code syntax."; if(!checkCodeSyntax()) { qCritical() << "The Monomer code does not pass the syntax test."; error_list_p->push_back("The Monomer code does not pass the syntax test"); } Formula temp_formula(m_formula); if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(), error_list_p)) { qCritical() << "The Monomer formula failed to validate."; error_list_p->push_back("The Monomer formula failed to validate"); } double mono = 0.0; double avg = 0.0; if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg)) { qCritical() << "Failed to calculate the Monomer masses."; error_list_p->push_back("Failed to calculate the Monomer masses"); } qDebug() << qSetRealNumberPrecision(6) << "Validation calculated masses (not member data):" << mono << "-" << avg; for(const ModifSPtr &modif_sp : m_modifs) { ErrorList local_error_list; if(!modif_sp->validate(&local_error_list)) { qCritical() << "Monomer's Modif" << modif_sp->getName() << "failed to validate successfully."; error_list_p->push_back(QString("Failed to validate Monomer's Modif %1") .arg(modif_sp->getName())); } } // If we added errors, then that means that the Monomer was not valid. m_isValid = (error_list_p->size() > error_count ? false : true); return m_isValid; } /*! \brief Returns the validity status of this Monomer. \sa validate() */ bool Monomer::isValid() const { return m_isValid; } //////////////// MASS OPERATIONS ///////////////////// /*! \brief Calculates this monomer's monoisotopic and avg masses and sets the results to \a mono and \a avg. The calculation is performed by computing the masses of this monomer's formula, accounting the chemical entities defined by \a monomer_chemical_entities. The calculations are performed using reference data in \a isotopic_data_csp. Returns true if the calculations were successful, false otherwise. \sa Formula::accountMasses() */ bool Monomer::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, double &mono, double &avg, Enums::ChemicalEntity monomer_chemical_entities) const { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; m_isValid = false; return m_isValid; } } mono = 0; avg = 0; bool ok; // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg); if(!ok) { qCritical() << "Failed accounting masses for Monomer:" << m_name << "and formula:" << m_formula; m_isValid = false; return m_isValid; } if(static_cast(monomer_chemical_entities) & static_cast(Enums::ChemicalEntity::MODIF)) { for(const ModifSPtr &modif_sp : m_modifs) modif_sp->accountMasses(mono, avg, /*times*/ 1); } return true; } /*! \brief Calculates this monomer's monoisotopic and avg masses. The calculation is performed by computing the masses of this monomer's formula, accounting or not the entities described by \a monomer_chemical_entities. The reference data for the computations are accessed at \a isotopic_data_csp. Returns true if the calculations were successful, false otherwise. \sa Formula::accountMasses() */ bool Monomer::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp, Enums::ChemicalEntity monomer_chemical_entities) { IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp; if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { if(mcsp_polChemDef != nullptr) local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); if(local_isotopic_data_csp == nullptr || local_isotopic_data_csp.get() == nullptr) { qCritical() << "Failed to find usable isotopic data."; m_isValid = false; return m_isValid; } } m_mono = 0; m_avg = 0; bool ok; // Formula temp_formula(m_formula); Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, m_mono, m_avg); if(!ok) { qCritical() << "Failed accounting masses for Monomer:" << m_name << "and formula:" << m_formula; m_isValid = false; return m_isValid; } // qDebug() << "After calculating the masses: the Monomer:" << toString(); // qDebug() << "flags:" << static_cast(monomer_chemical_entities) // << chemicalEntityMap[monomer_chemical_entities]; if(static_cast(monomer_chemical_entities) & static_cast(Enums::ChemicalEntity::MODIF)) { for(const ModifSPtr &modif_sp : m_modifs) modif_sp->accountMasses(&m_mono, &m_avg, /*times*/ 1); } return true; } /*! \brief Calculates this monomer's monoisotopic and avg masses. The calculation is performed by computing the masses of this monomer's formula. If \a monomer_chemical_entities & MONOMER_CHEMENT_MODIF is true, then the masses are updated to account for the mass of modifications. Set \a ok to true if the calculations were successful, false otherwise. Returns this object. \sa Formula::accountMasses() */ Monomer & Monomer::calculateMasses(bool &ok, const IsotopicDataCstSPtr &isotopic_data_csp, Enums::ChemicalEntity monomer_chemical_entities) { ok = calculateMasses(isotopic_data_csp, monomer_chemical_entities); return *this; } /*! \brief Increases \a mono_p and \a avg_p by the corresponding member masses first compounded by \a times. Returns true. */ const Monomer & Monomer::accountMasses(double *mono_p, double *avg_p, int times) const { if(mono_p != nullptr) *mono_p += m_mono * times; if(avg_p != nullptr) *avg_p += m_avg * times; return *this; } /*! \brief Increases \a mono and \a avg by the corresponding member masses first compounded by \a times. Returns true. */ const Monomer & Monomer::accountMasses(double &mono, double &avg, int times) const { mono += m_mono * times; avg += m_avg * times; return *this; } /*! \brief Returns the mass of the type defined by \a mass_type. */ double Monomer::getMass(Enums::MassType mass_type) const { if(mass_type == Enums::MassType::MONO) return m_mono; else if(mass_type == Enums::MassType::AVG) return m_avg; qFatalStream() << "Mass cannot be anything else than Enums::MassType::MONO or Enums::MassType::AVG."; return -1; } //////////////// FORMULA OPERATIONS ///////////////////// /* \brief Calculates a Formula representing this monomer . The calculated formula accounts for this monomer's formula and for its modifications formulas if any and if \a (monomer_chemical_entities & MONOMER_CHEMENT_MODIF) is true. This monomer's formula must validate using Modif::validate. Returns the Formula. \sa Modif::accountFormula() */ QString Monomer::calculateFormula(Enums::ChemicalEntity monomer_chemical_entities) const { // We want to return the calculated formula of this monomer that accounts // for its modifications if so is requested. IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // qDebug() << "Calculating formula for monomer: " << m_name //<< "with chemical entities:" << monomer_chemical_entities; Formula formula(m_formula); // The m_formula above is only a text string. We need to convert that // into the symbol/count pair by validating it with true true params. // The formula is asked to validate with storage of the found symbol/count // pairs and with resetting of the previous contents of the symbol/count // map. // We need to seed the symbol/count pairs because the following call // (accountFormula()) will update the pairs' values. ErrorList error_list; if(!formula.validate( isotopic_data_csp, /*store*/ true, /*reset*/ true, &error_list)) { qDebug() << "Formula:" << formula.getActionFormula() << "failed to validate with errors:" << Utils::joinErrorList(error_list, ", "); return QString(); } bool ok = false; if(static_cast(monomer_chemical_entities) & static_cast(Enums::ChemicalEntity::MODIF)) { for(const ModifSPtr &modif_sp : m_modifs) { formula.accountFormula(modif_sp->formula(/*with_title*/ false), mcsp_polChemDef->getIsotopicDataCstSPtr(), 1, ok); if(!ok) return QString(); } } return formula.getActionFormula(); } /*! \brief Parses the monomer XML \a element specifically for \a version. Parses the monomer XML element passed as argument and for each encountered data will set the data to this monomer (this is called XML rendering).The parsing is delegated to a function that is specific for for \a version of the polymer chemistry definition. The XML element is found in the polymer chemistry definition and has the following form: \code Glycine G C2H3N1O1 Alanine A C3H5N1O1 \endcode After setting all the data, this monomer calculates it masses and validates itself. If any of these steps fails, the error is reported by returning false. \sa validate() */ bool Monomer::renderXmlMnmElement(const QDomElement &element, [[maybe_unused]] int version) { /* In a polymer chemistry definition, the xml node we are in is * structured this way: * * * Glycine * G * C2H3N1O1 * * * And the element parameter points to the * * element tag: * ^ * | * +----- here we are right now. * * Which means that element.tagName() == "mnm" and that we'll have * to go one step down to the first child of the current node in * order to get to the element. * */ m_isValid = true; // QString str; // QTextStream stream(&str); // QDomNode node = element; // node.save(stream, 2 /*indent*/); // qDebug().noquote() << "The element:\n" << str; if(element.tagName() != "mnm") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } QDomElement child; child = element.firstChildElement("name"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The Monomer did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_name = child.text(); // qDebug() << "The name:" << m_name; child = child.nextSiblingElement("code"); if(child.isNull()) { qCritical() << "The Monomer did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } m_code = child.text(); // qDebug() << "The code:" << m_code; if(!checkCodeSyntax()) { qCritical() << "The Monomer did not render correctly: problem with the " "code text."; m_isValid = false; return m_isValid; } child = child.nextSiblingElement("formula"); if(child.isNull()) { qCritical() << "The Monomer did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } Formula temp_formula; if(!temp_formula.renderXmlFormulaElement(child)) { qCritical() << "The Monomer did not render correctly: the formula did not " "render correctly."; m_isValid = false; return m_isValid; } m_formula = temp_formula.getActionFormula(/*with_title*/ true); // qDebug() << "The formula:" << m_formula; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The Monomer did not validate successfully after " "rendering, with errors:"; Utils::joinErrorList(error_list, ", "); } else { // At this point, because we are creating a Monomer from scratch, // and not by copying or by assignment, we calculate the masses // explicitely (validate() does that but not on member m_mono/m_avg // because the method is const.). if(!calculateMasses(nullptr)) { qCritical() << "The Monomer's masses could not be calculated."; } } qDebug() << "Correctly rendered Monomer" << m_name << ":" << toString(); return m_isValid; } /*! \brief Formats this monomer's data as a string suitable to be used as an XML element in the polymer chemistry definition. The typical monomer element that is generated in this function looks like this: \code Glycine G C2H3N1O1 \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a dynamically allocated string that needs to be freed after use. */ QString Monomer::formatXmlMnmElement(int offset, const QString &indent) const { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n").arg(lead).arg(m_code); text += QString("%1%2\n").arg(lead).arg(m_formula); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } /*! \brief Parses into this monomer the XML monomer \a element passed as argument. The XML element comes from a polymer sequence file, where the monomer is singled out (not in a sequence string) because it might be modified, like this: \code S Phosphorylation H1O3P1 * 1 GRKASGSSPTSPINADKVENEDAFLEEVAEEKPHVKPYFTKTILDMEVVEGSAARFDCKIEGYPDPEVM W Oxidation O1 * 1 YKDDQPVKESRHFQIDYDEEGNCSLTISEVCGDDDAKYTCKAVNSLGEATCTAELLVETMGKEGEGEGEGEEDEEEEEE \endcode \a version indicates the format version of this XML \a element. As soon as the monomer code is known, while parsing the \a element, the corresponding monomer is searched in the list of monomers in the member polymer chemistry definition (\c mcsp_polChemDef). Then, the found monomer is copied into \c this monomer so that both monomers are identical, effectively initializing this monomer to the monomer described by the \a element. If the \a element contains one or more \c mdf modifications, these modifications are allocated as \l{Modif}'s and validated. If these modifications validate successfully, they are appended to this monomer's list of modifications. Returns true if initialization of his monomer with the contents of \a element succeeded, false otherwise. \sa formatXmlMonomerElement(int offset, const QString &indent) */ bool Monomer::renderXmlMonomerElement(const QDomElement &element, [[maybe_unused]] int version) { qDebug(); m_isValid = true; // QString str; // QTextStream stream(&str); // QDomNode node = element; // node.save(stream, 2 /*indent*/); // qDebug().noquote() << "The element:\n" << str; if(element.tagName() != "monomer") { qCritical() << "The expected element is not found."; m_isValid = false; return m_isValid; } qDebug() << "Indeed a monomer element."; QDomElement child; child = element.firstChildElement("code"); if(child.isNull() || child.text().isEmpty()) { qCritical() << "The Monomer did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } QString code = child.text(); qDebug() << "The code:" << code; // Use the code to access the corresponding Monomer in the list of Monomers // in the polymer chemistry definition. MonomerSPtr monomer_csp = mcsp_polChemDef->getMonomerCstSPtrByCode(code); qDebug() << "monomer pointer:" << monomer_csp.get(); if(monomer_csp == nullptr) { qCritical() << "The monomer's code is not found in the polymer chemistry " "definition."; m_isValid = false; return m_isValid; } qDebug() << "monomer name:" << monomer_csp->getName(); *this = *monomer_csp; // Sanity check if(m_code != code) qFatal("Programming error. Both codes should be identical."); // And now we have to manage the mdf objects. child = child.nextSiblingElement(); while(!child.isNull()) { if(child.tagName() != "mdf") { qCritical() << "The Monomer did not render correctly: problem with the " " element."; m_isValid = false; return m_isValid; } Modif modif(mcsp_polChemDef, child, version); qDebug() << "20250428-Created Modif:" << modif.getName() << "with Formula:" << modif.getFormula(); if(!modif.calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr())) { qCritical() << "Failed to calculate masses for Monomer's Modif" << modif.getName(); m_isValid = false; return m_isValid; } // The validation will take care of checking that the // element did have correct text inside. ErrorList error_list; if(!modif.validate(&error_list)) { qCritical() << "Failed to validate modification" << modif.getName() << "with errors:" << Utils::joinErrorList(error_list, ", "); m_isValid = false; return m_isValid; } error_list.clear(); // qDebug() << "At this point, going to modify the monomer."; QString uuid = modify(modif, /*override*/ false, &error_list); // qDebug() << "The returned uuid:" << uuid; if(uuid.isEmpty()) { qCritical() << "The monomer could not be modified, with errors:" << Utils::joinErrorList(error_list, ", "); m_isValid = false; return m_isValid; } child = child.nextSiblingElement(); } m_isValid = true; return m_isValid; } /*! \brief Formats a string suitable to be used as an XML element in a polymer sequence file. The typical monomer element that is generated in this function looks like this: \code S MODIF Phosphorylation COMMENT Phosphorylation is only partial \endcode The formatting of the XML element takes into account \a offset and \a indent by prepending the string with \a offset * \a indent character substring. \a indent defaults to two spaces. Returns a string. */ QString Monomer::formatXmlMonomerElement(int offset, const QString &indent) const { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } text.append(QString("%1\n").arg(lead)); // Prepare the lead for the child that is indented. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } QString code_element_string = QString("%1%2\n").arg(lead).arg(m_code); qDebug() << "code element:" << code_element_string; text.append(code_element_string); // The monomer may have any number of modif objects, which we have // to document here. // Continue with indented element(s) (same indent as for ). for(const ModifSPtr &modif_sp : m_modifs) text.append(modif_sp->formatXmlMdfElement(newOffset, indent)); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text.append(QString("%1\n").arg(lead)); // QString debug_message = // QString("%1\n%2\n").arg("Returning string:").arg(text); // qDebug().noquote() << debug_message; return text; } //////////////// UTILS ///////////////////// /*! \brief Stores the Modif instance \a modif_sp pointer in the member container. The \a modif_sp is stored as is, without duplication. Returns the Uuid string associated to the stored Modif. */ QString Monomer::storeModif(const ModifSPtr &modif_sp) { if(modif_sp == nullptr) qFatalStream() << "The provided ModifSPtr is nullptr."; qDebug() << "Right before storage, there are currently" << m_modifs.size() << "modifications."; // Do not store an item twice. if(hasModif(modif_sp) || !getUuidForModif(modif_sp).isEmpty()) qFatalStream() << "It is prohibited to store the same ModifSPtr more than once."; qDebug() << "Ok, we can go on."; // Even if we get a ref to shared_ptr, the reference count increment will // occur. m_modifs.push_back(modif_sp); QString uuid = QUuid::createUuid().toString(); m_uuidModifPairs.push_back(UuidModifWPtrPair(uuid, modif_sp)); qDebug() << "Right after storage, there are currently" << m_modifs.size() << "modifications."; return uuid; } /*! \brief Stores the Modif instance \a modif in the member container. The \a modif is used to craft a ModifSPtr that is stored. Returns the Uuid string associated to the stored Modif. */ QString Monomer::storeModif(const Modif &modif) { ModifSPtr modif_sp = std::make_shared(modif); if(modif_sp == nullptr) { qFatalStream() << "Failed to allocate ModifSPtr."; return ""; } return storeModif(modif_sp); } /*! \brief Returns true if \a modif_sp was found in the member container of Modif instances, false otherwise. */ bool Monomer::hasModif(const ModifSPtr &modif_sp) const { if(modif_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_modifs.cbegin(), m_modifs.cend(), [modif_sp](const ModifSPtr &the_modif_sp) { return the_modif_sp == modif_sp; }); if(the_iterator_cst == m_modifs.cend()) { std::vector::const_iterator the_pair_iterator_cst = std::find_if(m_uuidModifPairs.cbegin(), m_uuidModifPairs.cend(), [modif_sp](const UuidModifWPtrPair &the_pair) { return the_pair.second.lock() == modif_sp; }); if(the_pair_iterator_cst != m_uuidModifPairs.cend()) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; return false; } return true; } /*! \brief Returns true if \a modif_sp was found in the member container of Uuid-Modif pairs, false otherwise. */ bool Monomer::hasUuid(const ModifSPtr &modif_sp) const { if(modif_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidModifPairs.cbegin(), m_uuidModifPairs.cend(), [modif_sp](const UuidModifWPtrPair &the_pair) { return the_pair.second.lock() == modif_sp; }); if(the_iterator_cst == m_uuidModifPairs.cend()) return false; // Sanity check if(!hasModif(modif_sp)) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; return true; } /*! \brief Returns the Monomer instance pointer in the member container that is associated to the \a uuid Uuid string. If no such Monomer instance pointer is found, nullptr is returned. */ ModifSPtr Monomer::getModifForUuid(const QString &uuid) const { qDebug() << "There are currently" << m_modifs.size() << "modifications. The Modif uuid that is asked for:" << uuid; std::vector>::const_iterator the_iterator_cst = std::find_if(m_uuidModifPairs.cbegin(), m_uuidModifPairs.cend(), [uuid](const UuidModifWPtrPair &the_pair) { qDebug() << "Iterating into" << the_pair.first << "while searching for" << uuid; return the_pair.first == uuid; }); if(the_iterator_cst == m_uuidModifPairs.cend()) return nullptr; ModifSPtr modif_sp = (*the_iterator_cst).second.lock(); // Sanity check if(!hasModif(modif_sp)) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; return modif_sp; } /*! \brief Returns the UUID string identifying \a modif_sp in the member container. If no such Modif is found, an empty string is returned. */ QString Monomer::getUuidForModif(const ModifSPtr &modif_sp) const { if(modif_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidModifPairs.cbegin(), m_uuidModifPairs.cend(), [modif_sp](const UuidModifWPtrPair &the_pair) { // Do not query the modif_sp managed object because it can // be nullptr! return the_pair.second.lock() == modif_sp; }); if(the_iterator_cst == m_uuidModifPairs.cend()) { // Sanity check if(hasModif(modif_sp)) qFatalStream() << "Inconsistency between the m_modifs and the " "m_uuidModifPairs vectors."; return QString(); } // Sanity check if(!hasModif(modif_sp)) qFatalStream() << "Inconsistency between the m_modifs and the m_uuidModifPairs vectors."; return (*the_iterator_cst).first; } /*! \brief Returns a container of QString instances that correspond to the UUID strings that identify all the Modif instances involved in this Monomer. If no Modif is found, an empty container is returned. */ std::vector Monomer::getAllModifUuids() const { std::vector the_uuid_strings; for(const UuidModifCstWPtrPair pair : m_uuidModifPairs) the_uuid_strings.push_back(pair.first); // Sanity check if(the_uuid_strings.size() != m_modifs.size()) qFatalStream() << "Inconsistency between the _s and pairs."; return the_uuid_strings; } void Monomer::cleanupModifs() { qDebug() << "At beginning, count of UUID-Modif pairs:" << m_uuidModifPairs.size(); std::vector::iterator the_iterator = m_uuidModifPairs.begin(); std::vector::iterator the_end_iterator = m_uuidModifPairs.end(); while(the_iterator != the_end_iterator) { if((*the_iterator).second.expired() || (*the_iterator).second.lock() == nullptr || !hasModif((*the_iterator).second.lock())) the_iterator = m_uuidModifPairs.erase(the_iterator); else ++the_iterator; } qDebug() << "At end, count of UUID-Modif pairs:" << m_uuidModifPairs.size(); } bool Monomer::removeModif(ModifSPtr modif_sp) { if(modif_sp == nullptr || modif_sp.get() == nullptr) qFatalStream() << "Cannot be that pointer is nullptr."; // We will need this anyway. QString uuid = getUuidForModif(modif_sp); // Some controls are in order: we have to check that at index, there is // a ModifSPtr that is present both in m_modifs and in // m_uuidModifPairs. std::vector::const_iterator the_iterator_cst = std::find_if( m_modifs.begin(), m_modifs.end(), [modif_sp](ModifSPtr iter_modif_sp) { return iter_modif_sp == modif_sp; }); if(the_iterator_cst == m_modifs.end()) { qCritical() << "The ModifSPtr was not found in the container."; if(!uuid.isEmpty()) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; return false; } // At this point, both containers contain modif_sp. m_modifs.erase(the_iterator_cst); std::vector::const_iterator the_pair_iterator_cst = std::find_if(m_uuidModifPairs.cbegin(), m_uuidModifPairs.cend(), [uuid](const UuidModifWPtrPair &the_pair) { // Do not query the modif_sp managed object because it can // be nullptr! return the_pair.first == uuid; }); if(the_pair_iterator_cst == m_uuidModifPairs.cend()) qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs."; m_uuidModifPairs.erase(the_pair_iterator_cst); return true; } /*! \brief Clears all the data of the Monomer instance. */ void Monomer::clear() { mcsp_polChemDef = nullptr; m_name = ""; m_code = ""; m_formula = ""; m_mono = 0.0; m_avg = 0.0; m_modifs.clear(); m_isValid = false; } /*! \brief Returns a text string representing this Monomer instance. */ QString Monomer::toString() const { return QString( "Monomer: %1 - %2 - %3 - %4 - %5 - modifs: %6 - is valid: %7\n") .arg(m_name) .arg(m_code) .arg(m_formula) .arg(m_mono, 0, 'f', ATOM_DEC_PLACES) .arg(m_avg, 0, 'f', ATOM_DEC_PLACES) .arg(m_modifs.size()) .arg(m_isValid); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/MonomerDictionary.cpp000664 001750 001750 00000036311 15100504560 026435 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/MonomerDictionary.hpp" #include "MsXpS/libXpertMassCore/Sequence.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::MonomerDictionary \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile MonomerDictionary.hpp \brief The MonomerDictionary class provides a Monomer code dictionary allowing the user to automatically translate Monomer codes from x-letter codes to y-letter codes. For example, a monomer dictionary file can define how to translate 3-letter monomer codes to 1-letter codes. This is typically useful when working on Protein Database (PDB) file. The format of the dictionary file is the following: \code # Converts from code on the left of '>' to code on the right. # Number of letters allowed in each code is # described with syntax 3>1 and that line should be the first # non-comment line in this file. 3>1 ALA>A CYS>C ASP>D \endcode There might be more than one \e{section} in the file, with, for example, 3>1 translations and then 1>3 translations. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_filePath \brief Path to the file documenting the translations. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_dictionaryHash \brief The hash that documents the translations. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_dictionaryLoaded \brief Indicates if the dictionary file has been loaded already. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_inputChainStringList \brief The list of sequences to be converted. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_inputCodeLength \brief The count of letters in the input Monomer code. In a dictionary file that has a section \code 3>1 \endcode this value would be \e 3. */ /*! \variable MsXpS::libXpertMassCore::MonomerDictionary::m_outputCodeLength \brief The count of letters in the output Monomer code. In a dictionary file that has a section \code 3>1 \endcode this value would be \e 1. */ /*! \brief Constructs a MonomerDictionary instance. \list \li \a file_path: the path to the file containing the Monomer dictionary. \li \a input_chain_string_list: the list of sequences to be converted. \li \a input_code_length: the count of letters in the Monomer codes in the input string. \li \a output_code_length: the count of letters in the Monomer codes in the output string. \endlist */ MonomerDictionary::MonomerDictionary(QString file_path, const QStringList &input_chain_string_list, int input_code_length, int output_code_length) : m_filePath(file_path), m_inputChainStringList(input_chain_string_list), m_inputCodeLength(input_code_length), m_outputCodeLength(output_code_length) { } /*! \brief Destructs this MonomerDictionary instance. */ MonomerDictionary::~MonomerDictionary() { } /*! \brief Sets the \a file_path to the Monomer dictionary file. */ void MonomerDictionary::setFilePath(QString &file_path) { m_filePath = file_path; } /*! \brief Sets the list of input sequences to \a input_chain_string_list. */ void MonomerDictionary::setInputChainStringList( const QStringList &input_chain_string_list) { m_inputChainStringList = input_chain_string_list; } /*! \brief Set the count of letters in the input Monomer codes to \a code_length. */ void MonomerDictionary::setInputCodeLength(int code_length) { m_inputCodeLength = code_length; } /*! \brief Set the count of letters in the output Monomer codes to \a code_length. */ void MonomerDictionary::setOutputCodeLength(int code_length) { m_outputCodeLength = code_length; } /*! \brief Return true if the \a line parsed is in the form X>Y, that is, that it specifies the kind of Monomer code translation. */ bool MonomerDictionary::isLineProperSectionDivider(const QString &line) { // Section dividers in the monomer dictionary file format are // lines containing the following syntax: X>Y, that is for example // 3>1. This means that the following translation rules (like // ILE>I) should convert 3-letter codes into 1-letter codes. // However, this line should only be considered proper if X is // actually the value of m_inputCodeLength and Y the value of // m_outputCodeLength. // qDebug() << __FILE__ << __LINE__ // << "Checking if line is proper section divider :" << line; if(line.contains(QRegularExpression("[0-9]+>[0-9]+"))) { // We are opening a new section, get the input/output code // lengths and if they math what we expect, then set the // current stream position and call the section parser. int greaterThanIndex = line.indexOf('>'); QString codeLengthString = line.left(greaterThanIndex); // qDebug() << __FILE__ << __LINE__ // << "Left codeLengthString:" << codeLengthString // << "m_inputCodeLength:" << m_inputCodeLength; bool ok = false; int codeLength = codeLengthString.toInt(&ok, 10); if(!codeLength && !ok) { qDebug() << __FILE__ << __LINE__ << "Monomer dictionary" << "Failed to parse file " << m_filePath << "at line " << line; return false; } if(codeLength != m_inputCodeLength) { return false; } codeLengthString = line.mid(greaterThanIndex + 1, -1); // qDebug() << __FILE__ << __LINE__ // << "Right codeLengthString:" << codeLengthString // << "m_outputCodeLength:" << m_outputCodeLength; ok = false; codeLength = codeLengthString.toInt(&ok, 10); if(!codeLength && !ok) { qDebug() << __FILE__ << __LINE__ << "Monomer dictionary" << "Failed to parse file " << m_filePath << "at line " << line; return false; } if(codeLength != m_outputCodeLength) { return false; } // At this point, it seems we are in the proper // section. return true; } // If we are here, that means that the section is not for us. // qDebug() << __FILE__ << __LINE__ // << "Line is no proper section divider."; return false; } void MonomerDictionary::skipSection(QTextStream *stream) { // We have entered a section, all we have to do is go through it // and return when we have found either the end of the stream or // the {END} marker. qint64 lineLength = 1024; QString line; while(!stream->atEnd()) { line = stream->readLine(lineLength); if(!line.contains("{END}")) { line = stream->readLine(lineLength); } else return; } } /*! \brief Parses the Monomer dictionary file section in \a stream and fills in the \l m_dictionaryHash with the translation pair. */ int MonomerDictionary::parseSection(QTextStream *stream) { Q_ASSERT(stream); qint64 lineLength = 1024; QString line; // Iterate in the file using the stream and for each line create // an item to insert into the dictionary hash. while(!stream->atEnd()) { line = stream->readLine(lineLength); // We might encounter the end of the section, that is a line // having {END} as its sole content. if(line.contains("{END}")) break; QStringList stringList = line.split('>'); QString inputCode = stringList.first(); QString outputCode = stringList.last(); // Check that the monomer codes have the proper length. if(inputCode.length() != m_inputCodeLength || outputCode.length() != m_outputCodeLength) { qDebug() << __FILE__ << __LINE__ << QObject::tr("Monomer dictionary:") << QObject::tr("Failed to load dictionary.") << QObject::tr("Monomer code lengths do not match:") << QObject::tr("inputCode:") << inputCode << QObject::tr("outputCode:") << outputCode; // We have to empty the hash m_dictionaryHash.clear(); break; } m_dictionaryHash.insert(inputCode, outputCode); // qDebug() << __FILE__ << __LINE__ // << stringList.first () << stringList.last (); } // At this point the parsing is finished, either because we // encountered the {END} section-ending delimiter, or because we // reached the en of file. int hashSize = m_dictionaryHash.size(); if(hashSize) m_dictionaryLoaded = true; else { qDebug() << __FILE__ << __LINE__ << QObject::tr("Monomer dictionary:") << QObject::tr("Failed to load dictionary."); m_dictionaryLoaded = false; } return hashSize; } /*! \brief Returns true if the Monomer dictionary file could be loaded successfully, false otherwise. */ bool MonomerDictionary::loadDictionary() { // Load the file and for each line deconstruct the item into two // QString objects that are used to make a QHash entry in // QHash m_dictionaryHash. bool success = true; qint64 lineLength = 1024; QString line; QFile file(m_filePath); if(!file.open(QIODevice::ReadOnly)) { m_dictionaryLoaded = false; qDebug() << __FILE__ << __LINE__ << "Monomer dictionary:" << "Failed to open file" << m_filePath << "for writing."; return false; } if(m_inputCodeLength < 1 || m_outputCodeLength < 1) { qDebug() << __FILE__ << __LINE__ << "Monomer dictionary:" << "Failed to parse file " << m_filePath << "Please, set the m_inputCodeLength and " "m_ouputCodeLength variables first."; return false; } QTextStream *stream = new QTextStream(&file); stream->setEncoding(QStringConverter::Utf8); while(!stream->atEnd()) { line = stream->readLine(lineLength); // qDebug() << __FILE__ << __LINE__ // << "line: " << line; // Remove spaces from start and end of line. line = line.simplified(); if(line.startsWith('#') || line.isEmpty()) { line = stream->readLine(lineLength); continue; } // There might be any number of sections in the file, all // delimited with a X>Y directive, indicating how many // characters are allowed for the input code and for the // output code. if(!isLineProperSectionDivider(line)) { // qDebug() << __FILE__ << __LINE__ // << "skipping line:" << line; line = stream->readLine(lineLength); continue; } else { // qDebug() << __FILE__ << __LINE__ // << "parsing section: " << line; if(parseSection(stream) < 1) { qDebug() << __FILE__ << __LINE__ << "Monomer dictionary:" << "Failed to parse file " << m_filePath; success = false; break; } else { // We successfully parsed the section. Our work is done. success = true; break; } } } delete stream; return success; } /*! \brief Perform the actual translation from the input Monomer code form to the output Monomer code form on all the strings contained in \a chain_string_list. */ QStringList * MonomerDictionary::translate(const QStringList &chain_string_list) { // The string in sequence is a space-separated list of monomer // codes in the original monomer code format. We have to translate // that to the proper monomer code format using the hash in this // dictionary. QStringList *outputChainStringList = new QStringList(); if(!chain_string_list.isEmpty()) m_inputChainStringList = chain_string_list; // If there is nothing to do return an empty string list so that // caller knows nothing is actually wrong, only there is no // sequence to translate. if(m_inputChainStringList.isEmpty()) return outputChainStringList; // Iterate in each chain string of the list and perform the // translation. for(int iter = 0; iter < m_inputChainStringList.size(); ++iter) { QString iterString = chain_string_list.at(iter); // qDebug() << __FILE__ << __LINE__ // << "translating sequence:" << iterString; QStringList codeList = iterString.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); // qDebug() << __FILE__ << __LINE__ // << "codeList:" << codeList; // qDebug() << __FILE__ << __LINE__ // << "hash:" // << m_dictionaryHash; for(int jter = 0; jter < codeList.size(); ++jter) { QString code = codeList.at(jter); QHash::const_iterator hashIter = m_dictionaryHash.find(code); if(hashIter != m_dictionaryHash.end()) codeList.replace(jter, hashIter.value()); else { // Delete the string list, set the pointer to 0 and // return that pointer so that caller knows something // has gone wrong. qDebug() << __FILE__ << __LINE__ << "Monomer dictionary:" << "Failed to convert monomer code " << code; outputChainStringList->clear(); delete outputChainStringList; outputChainStringList = nullptr; return outputChainStringList; } } // At this point the sequence codes have been translated. Join all // the item of the codeList into one single string. outputChainStringList->append(codeList.join(QString(""))); } // End of // for (int iter = 0; iter < chainStringList.size(); ++iter) // If no translation could be performed, return a n if(!outputChainStringList->size()) { outputChainStringList->clear(); delete outputChainStringList; outputChainStringList = 0; } return outputChainStringList; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Oligomer.cpp000664 001750 001750 00000075754 15100504560 024566 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Oligomer.hpp" #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Ionizer.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Oligomer \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Oligomer.hpp \brief The Oligomer class provides abstractions to work with an oligomer molecule (peptide or oligonucleotide, for example). The notion of an \l Oligomer is that it is part of a \l Polymer and is thus defined by a range of \l Monomer indices in this \l Polymer (the \l IndexRangeCollection). The start index cannot be less than 0 nor greater than the size of the polymer minus one, and the end index follows the same rule. An \l Oligomer is also characterized by a monoisotopic mass and an average mass. The ionization of the Oligomer is handled by the member \l Ionizer instance. All computations about an oligomer (fragmentation, composition, for example, isoelectric point, ...) can only be performed by referring to the sequence of its "enclosing" \l Polymer. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::mcsp_polymer \brief The \l Polymer instance of which this Oligomer is part. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_name \brief The name of the Oligomer. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_description \brief The description of the Oligomer. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_isModified \brief Tells if the Oligomer is modified. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_ionizer \brief The Ionizer instance that drives the ionization of the Oligomer. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_calcOptions \brief The CalcOptions instance that drives the mass and formula calculations. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_mono \brief The monoisotopic mass. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_avg \brief The average mass. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_crossLinks \brief The container of CrossLink instances formed in the Oligomer sequence. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_partialCleavage \brief Stores the partial cleavage out of which the Oligomer has been obtained if that process was a (bio)-chemical cleavage. */ /*! \variable MsXpS::libXpertMassCore::Oligomer::m_formula \brief Stores the formula of the Oligomer. */ /*! \typedef MsXpS::libXpertMassCore::OligomerSPtr \relates Oligomer Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::OligomerCstSPtr \relates Oligomer Synonym for std::shared_ptr. */ /*! \brief Constructs an Oligomer. The Oligomer instance is constructed with these arguments: \list \li \a polymer_cqsp The \l Polymer instance that encloses this Oligomer. \li \a name The name of this Oligomer, used to intialize the Ionizable base class \li \a description The description of this Oligomer (m_description) \li \a is_modified Tells if the Oligomer is modified \li \a ionizer The Ionizer used to ionize this Oligomer \li \a calc_options Used to initialize the m_calcOptions member \endlist */ Oligomer::Oligomer(PolymerCstQSPtr polymer_cqsp, const QString &name, const QString &description, bool is_modified, const Ionizer &ionizer, const CalcOptions &calc_options) : mcsp_polymer(polymer_cqsp), m_name(name), m_description(description), m_isModified(is_modified), m_ionizer(ionizer), m_calcOptions(calc_options) { qDebug() << "Allocating new Oligomer with other calc options:" << calc_options.toString() << "and this calc options:" << m_calcOptions.toString(); } /*! \brief Constructs the Oligomer as a copy of \a other. */ Oligomer::Oligomer(const Oligomer &other) : PropListHolder(other), mcsp_polymer(other.mcsp_polymer), m_name(other.m_name), m_description(other.m_description), m_isModified(other.m_isModified), m_ionizer(other.m_ionizer), m_calcOptions(other.m_calcOptions), m_mono(other.m_mono), m_avg(other.m_avg), m_crossLinks(other.m_crossLinks), m_partialCleavage(other.m_partialCleavage), m_formula(other.m_formula) { } /*! \brief Destructs this Oligomer. */ Oligomer::~Oligomer() { // qDebug() << "~Oligomer()"; } //////////////// THE POLYMER ///////////////////// /*! \brief Set the \l{PolChemDef} \l{mcsp_polymer} member to \a polymer_cqsp. \sa getPolymerCstSPtr() */ void Oligomer::setPolymerCstSPtr(PolymerCstQSPtr polymer_cqsp) { mcsp_polymer = polymer_cqsp; } /*! \brief Returns the polymer. \sa getPolymerCstSPtr() */ const PolymerCstQSPtr & Oligomer::getPolymerCstSPtr() const { return mcsp_polymer; } //////////////// THE NAME ///////////////////// /*! \brief Sets the \a name. */ void Oligomer::setName(const QString &name) { m_name = name; } /*! \brief Returns the name. */ QString Oligomer::getName() const { return m_name; } //////////////// THE DESCRIPTION ///////////////////// /*! \brief Sets the \a description. */ void Oligomer::setDescription(const QString &description) { m_description = description; } /*! \brief Returns the description. */ QString Oligomer::getDescription() const { return m_description; } //////////////// MODIFICATION STATUS ///////////////////// /*! \brief Sets the modification status to \a is_modified. */ void Oligomer::setModified(bool is_modified) { m_isModified = is_modified; } /*! \brief Returns the chemical modification status of this Oligomer. If \a deep is true, the member Polymer must exist (mcsp_polymer cannot be nullptr) because the polymer itself is asked for the count of Monomer instances being modified. The sequence ranges of the polymer that are surveyed are those in this Oligomer's IndexRangeCollection. Upon checking the result, if the count of modified Monomer instances is not 0, then m_isModified is set to true, otherwise it is set to false. That m_isModified value is then returned. If \a deep is false, the value of m_isModified is immediately returned. \sa Polymer::hasModifiedMonomer, Monomer::isModified() */ bool Oligomer::isModified(bool deep /*false*/) const { // Either we truly go to the polymer instance and check if the oligomer is // modified or we just ask for the member datum, that might have been set, // for example, during creation of the oligomer in the Cleaver::cleave() // function. We need the possibility to ask for the member datum because // there are circumstances where the oligomer exists and not the original // polymer (for example if the polymer sequence is edited while a set of // cleavage oligomers is displayed in the cleavage dialog. When the // tableviewmodel needs to refresh the contents of the cells, it crashes // because the polymer has been edited and one monomer is missing from the // sequence of the oligomer as it had been configured in the first place. if(deep) { m_isModified = modifiedMonomerCount() != 0 ? true : false; } return m_isModified; } /*! \brief Return the count of the \l{Polymer}'s \l{Sequence}'s \l{Monomer} instances that are modified. \sa Polymer::modifiedMonomerCount */ std::size_t Oligomer::modifiedMonomerCount() const { if(mcsp_polymer == nullptr) qFatal("Programming error. The pointer cannot be nullptr."); return mcsp_polymer->modifiedMonomerCount( m_calcOptions.getIndexRangeCollectionCstRef()); } //////////////// THE IONIZER ///////////////////// /*! \brief Sets \a ionizer to the member datum. */ void Oligomer::setIonizer(const Ionizer &ionizer) { m_ionizer = ionizer; } /*! \brief Returns a const reference to the Ionizer. */ const Ionizer & Oligomer::getIonizerCstRef() const { return m_ionizer; } /*! \brief Returns a reference to the Ionizer. */ Ionizer & Oligomer::getIonizerRef() { return m_ionizer; } /*! \brief Returns the result of the ionization process. The new masses, if a change occurred, are updated in the member m_mono and m_avg variables. */ Enums::IonizationOutcome Oligomer::ionize() { return m_ionizer.ionize(m_mono, m_avg); } /*! \brief Returns the result of the deionization process. The new masses, if a change occurred, are updated in the member m_mono and m_avg variables. */ Enums::IonizationOutcome Oligomer::deionize() { return m_ionizer.deionize(m_mono, m_avg); } /*! \brief Sets \a mono and \a avg to the masses of unionized analyte. The member Ionizer is used to first deionize the \a mono and \a avg masses (on copy data, if the Ionizer reports that the analyte is ionized). \a mono and \a avg are then set to the masses of this polymer instance in an unionized status. Nothing is modified in this Oligomer instance. Returns the process outcome. */ Enums::IonizationOutcome Oligomer::molecularMasses(double &mono, double &avg) const { double temp_mono = m_mono; double temp_avg = m_avg; Enums::IonizationOutcome ionization_outcome = m_ionizer.deionize(temp_mono, temp_avg); if(ionization_outcome == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to deionize the analyte."; return ionization_outcome; } mono = temp_mono; avg = temp_avg; return ionization_outcome; } //////////////// THE SEQUENCE RANGES ///////////////////// /*! \brief Sets \a index_ranges to the member datum. */ void Oligomer::setIndexRanges(const IndexRangeCollection &index_ranges) { m_calcOptions.getIndexRangeCollectionRef().initialize(index_ranges); } /*! \brief Sets \a index_range to the member datum as the sole range in the member IndexRange container. */ void Oligomer::setIndexRange(const IndexRange &index_range) { m_calcOptions.getIndexRangeCollectionRef().clear(); m_calcOptions.getIndexRangeCollectionRef().setIndexRange(index_range); } /*! \brief Returns a const reference to the IndexRangeCollection. */ const IndexRangeCollection & Oligomer::getIndexRangeCollectionCstRef() const { return m_calcOptions.getIndexRangeCollectionCstRef(); } /*! \brief Returns a reference to the IndexRangeCollection. */ IndexRangeCollection & Oligomer::getIndexRangeCollectionRef() { return m_calcOptions.getIndexRangeCollectionRef(); } /*! \brief Sets the \a start_index and \a stop_index indices to the first IndexRange instance of the member IndexRangeCollection container. */ void Oligomer::setStartAndStopIndices(std::size_t start_index, std::size_t stop_index) { if(!m_calcOptions.getIndexRangeCollectionRef().size()) { m_calcOptions.getIndexRangeCollectionRef().setIndexRange(start_index, stop_index); } else { m_calcOptions.getIndexRangeCollectionRef() .getRangesRef() .front() ->m_start = start_index; m_calcOptions.getIndexRangeCollectionRef() .getRangesRef() .front() ->m_stop = stop_index; } } /*! \brief Sets \a start_index as the the first IndexRange instance'start value of the member IndexRangeCollection container. */ void Oligomer::setStartIndex(int start_index) { if(!m_calcOptions.getIndexRangeCollectionRef().size()) { m_calcOptions.getIndexRangeCollectionRef().setIndexRange(start_index, 0); } else { m_calcOptions.getIndexRangeCollectionRef() .getRangesRef() .front() ->m_start = start_index; } } /*! \brief Returns the start index, or 0 (\a ok is set to false) if the IndexRangeCollection member is empty. The returned start index is that of the first IndexRange instance in the IndexRangeCollection member (\a ok is set to true). */ qsizetype Oligomer::startIndex(bool &ok) const { if(!m_calcOptions.getIndexRangeCollectionCstRef().size()) { ok = false; return 0; } ok = true; return m_calcOptions.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() ->m_start; } /*! \brief Sets \a stop_index as the the first IndexRange instance'stop value of the member IndexRangeCollection container. */ void Oligomer::setStopIndex(int stop_index) { if(!m_calcOptions.getIndexRangeCollectionRef().size()) { m_calcOptions.getIndexRangeCollectionRef().setIndexRange(0, stop_index); } else { m_calcOptions.getIndexRangeCollectionRef() .getRangesRef() .front() ->m_stop = stop_index; } } /*! \brief Returns the stop index, or 0 (\a ok is set to false) if the IndexRangeCollection member is empty. The returned stop index is that of the first IndexRange instance in the IndexRangeCollection member (\a ok is set to true). */ qsizetype Oligomer::stopIndex(bool &ok) const { if(!m_calcOptions.getIndexRangeCollectionCstRef().size()) { ok = false; return 0; } ok = true; return m_calcOptions.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() ->m_stop; } //////////////// THE CALCULATION OPTIONS ///////////////////// /*! \brief Sets \a calc_options to the member datum. */ void Oligomer::setCalcOptions(const CalcOptions &calc_options) { m_calcOptions.initialize(calc_options); } /*! \brief Returns the CalcOptions member. */ const CalcOptions & Oligomer::getCalcOptionsCstRef() const { return m_calcOptions; } /*! \brief Returns a reference to the CalcOptions member. */ CalcOptions & Oligomer::getCalcOptionsRef() { return m_calcOptions; } //////////////// THE MASSES ///////////////////// /*! \brief Set the mass qualified using \a mass_type to the \a mass value. \sa setMasses() */ void Oligomer::setMass(Enums::MassType mass_type, double mass) { if(mass_type == Enums::MassType::MONO) m_mono = mass; else if(mass_type == Enums::MassType::AVG) m_avg = mass; else qFatalStream() << "The mass_type needs to be either MONO or AVG."; } /*! \brief Sets \a mono and \a avg masses to this Oligomer. \sa setMass() */ void Oligomer::setMasses(double mono, double avg) { m_mono = mono; m_avg = avg; } /*! \brief Returns the Oligomer's mass of the type defined by \a mass_type. */ double Oligomer::getMass(Enums::MassType mass_type) const { if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG) qFatalStream() << "The mass_type needs to be either MONO or AVG."; if(mass_type == Enums::MassType::MONO) return m_mono; return m_avg; } /*! \brief Returns a reference to the Oligomer's mass of the type defined by \a mass_type. */ double & Oligomer::getMassRef(Enums::MassType mass_type) { if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG) qFatalStream() << "The mass_type needs to be either MONO or AVG."; if(mass_type == Enums::MassType::MONO) return m_mono; return m_avg; } //////////////// THE CROSS-LINKS ///////////////////// /*! \brief Returns a const reference to the CrossLink container. */ const std::vector & Oligomer::getCrossLinksCstRef() const { return m_crossLinks; } /*! \brief Returns a reference to the CrossLink container. */ std::vector & Oligomer::getCrossLinksRef() { return m_crossLinks; } /*! \brief Adds the \a cross_link_sp CrossLink to this Oligomer's container of \l{CrossLink} instances. Returns true if the CrossLink was added succesfully, or false \a cross_link_sp was already in the container. */ bool Oligomer::addCrossLink(CrossLinkSPtr cross_link_sp) { if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; // Add the cross-link only if it does not exist already. Return true // only if the crossLink has been added. std::vector::const_iterator the_iterator_cst = std::find_if(m_crossLinks.cbegin(), m_crossLinks.cend(), [&cross_link_sp](const CrossLinkSPtr &iter_cross_link_sp) { return iter_cross_link_sp == cross_link_sp; }); if(the_iterator_cst == m_crossLinks.end()) { m_crossLinks.push_back(cross_link_sp); return true; } return false; } //////////////// THE PARTIAL CLEAVAGE ///////////////////// /*! \brief Set the member \l m_partialCleavage to \a partial_cleavage. */ void Oligomer::setPartialCleavage(std::size_t partial_cleavage) { m_partialCleavage = partial_cleavage; } /*! \brief Returns the m_partialCleavage member. */ std::size_t Oligomer::getPartialCleavage() const { return m_partialCleavage; } //////////////// THE FORMULA ///////////////////// /*! \brief Sets the formula to \a formula. */ void Oligomer::setFormula(const Formula &formula) { m_formula = formula; } /*! \brief Sets the formula to \a formula_string. */ void Oligomer::setFormula(const QString &formula_string) { m_formula = Formula(formula_string); } /*! \brief Returns a constant reference to the formula. */ const Formula & Oligomer::getFormulaCstRef() const { return m_formula; } /*! \brief Returns a reference to the formula. */ Formula & Oligomer::getFormulaRef() { return m_formula; } //////////////// THE MONOMERS ///////////////////// /*! \brief Returns the Monomer in the member Polymer that is located at the index that has the start value of the first IndexRange object in the member IndexRangeCollection container. If the Polymer is not avaialable, or if the start index could not be obtained, nullptr is returned. If the index is out of bounds, that is a fatal error. */ MonomerSPtr Oligomer::getLeftEndMonomerCstSPtr() const { if(mcsp_polymer == nullptr) { qCritical() << "The polymer is not available."; return nullptr; } bool ok = false; std::size_t index = startIndex(ok); if(!ok) return nullptr; if(index >= mcsp_polymer->getSequenceCstRef().size()) qFatalStream() << "Programming error. Index is out of bounds."; return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index); } /*! \brief Returns the Monomer in the member Polymer that is located at the index that has the stop value of the first IndexRange object in the member IndexRangeCollection container. If the Polymer is not avaialable, or if the stop index could not be obtained, nullptr is returned. If the index is out of bounds, that is a fatal error. */ MonomerSPtr Oligomer::getRightEndMonomerCstSPtr() const { if(mcsp_polymer == nullptr) { qCritical() << "The polymer is not available."; return nullptr; } bool ok = false; std::size_t index = stopIndex(ok); if(!ok) return nullptr; if(index >= mcsp_polymer->getSequenceCstRef().size()) qFatalStream() << "Programming error. Index is out of bounds."; return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index); } /*! \brief Returns the Monomer in the member Polymer that is located at the \a index. If the Polymer is not available, nullptr is returned. If \a index is not encompassed by the member IndexRangeCollection container, or if that container is empty, nullptr is returned. If \a index is out of bounds, that is a fatal error. */ MonomerSPtr Oligomer::getMonomerCstSPtrAt(std::size_t index) const { if(mcsp_polymer == nullptr) { qCritical() << "The polymer is not available."; return nullptr; } if(!m_calcOptions.getIndexRangeCollectionCstRef().size()) return nullptr; if(!m_calcOptions.getIndexRangeCollectionCstRef().encompassIndex(index)) { qCritical() << "Asking for Monomer at index not encompassed by member " "IndexRangeCollection."; } if(index >= mcsp_polymer->getSequenceCstRef().size()) qFatalStream() << "Programming error. Index is out of bounds."; return mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(index); } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this Oligomer instance. */ Oligomer & Oligomer::operator=(const Oligomer &other) { if(&other == this) return *this; PropListHolder::operator=(other); mcsp_polymer = other.mcsp_polymer; m_name = other.m_name; m_description = other.m_description; m_isModified = other.m_isModified; m_ionizer = other.m_ionizer; m_partialCleavage = other.m_partialCleavage; m_calcOptions.initialize(other.m_calcOptions); m_mono = other.m_mono; m_avg = other.m_avg; m_formula = other.m_formula; m_crossLinks.assign(other.m_crossLinks.cbegin(), other.m_crossLinks.cend()); return *this; } //////////////// ELEMENTAL CALCULATION FUNCTIONS ///////////////////// /*! \brief Returns the elemental composition of this Oligomer instance. The computation of the elemental composition is performed as configured in \a calc_options and using the ionization described in \a ionizer. */ QString Oligomer::elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const { qDebug().noquote() << "Calculating the elemental composition of Oligomer using " "CalcOptions:" << calc_options.toString() << "and Ionizer:" << ionizer.toString() << "that has internal formula:" << m_formula.getActionFormula(); if(calc_options.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS) qInfo() << "Enums::SelectionType::RESIDUAL_CHAINS"; else qInfo() << "Enums::SelectionType::OLIGOMERS"; qDebug() << "Going to call Polymer::elementalComposition() with CalcOptions:" << calc_options.toString() << "and ionizer:" << ionizer.toString(); return mcsp_polymer->elementalComposition(calc_options, ionizer); } /*! \brief Returns the elemental composition of this Oligomer instance. */ QString Oligomer::elementalComposition() const { return elementalComposition(m_calcOptions, m_ionizer); } //////////////// MASS CALCULATION FUNCTIONS ///////////////////// /*! \brief Calculates the monoisotopic and average masses. The calculation is performed by computing the mono and avg masses of the sequence stretch as described by the set of start and top indices (member IndexRangeCollection object) in the polymer sequence. The member CalcOptions (m_calcOptions) and the member Ionizer (m_ionizer) are used. The m_mono and m_avg member objects are reset to 0.0 before the calculations. Returns true if calculations were successful, false otherwise. */ bool Oligomer::calculateMasses() { return calculateMasses(m_calcOptions, m_ionizer); } /*! \brief Calculates the monoisotopic and average masses. The calculation is performed by computing the mono and avg masses of the sequence stretch as described by the start and end indices in the polymer sequence. The calculations are configured by \a calc_options and the ionization is defined in \a ionizer. The m_mono and m_avg member objects are reset to 0.0 before the calculations. The member Ionizer is not set to \a ionizer. This is so that a whole set of ionization computations can be performed without touching the internal Ionizer. Returns true if calculations were successful, false otherwise. */ bool Oligomer::calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer) { qDebug().noquote() << "Going to calculate masses with calculation options:" << calc_options.toString() << "and ionizer:" << ionizer.toString(); CalcOptions local_calc_options(calc_options); // The coordinates of the oligomer are the following: // MAMISGMSGRKAS // For a tryptic peptide obtained from protein above, we'd have // MAMISGMSGR, that is in the oligomer coordinates: // [0] MAMISGMSGR [9] // When computing the mass of the oligomer, we have to do a // for (iter == [0] ; iter < [9 + 1]; ++iter) // Which is why we increment add 1 to m_endIndex in the function below. // A polymer might be something made of more than one residual chain // in case it is a cross-linked oligomer. Compute the mass fore each // residual chain, without accounting for the cross-links... m_mono = 0; m_avg = 0; // An oligomer is characterized by a reference polymer and a // IndexRangeCollection object that documents the ranges of that Polymer // Sequence. That IndexRangeCollection object is in the calc_options object // passed as reference. With it we can call the Polymer::accountMasses // function. This is why we need to first copy the member IndexRangeCollection // object in it. // local_calc_options.setIndexRanges(m_calcOptions.getIndexRangeCollectionCstRef()); qDebug() << "Index ranges inside calculation options:" << local_calc_options.getIndexRangeCollectionRef().indicesAsText(); // We do not want to take into account the cross-links because // we'll be doing this here and because it cannot work if the // cross-links are taken into account from the polymer. int flags = static_cast(local_calc_options.getMonomerEntities()); // qDebug() << "flags:" << flags // << chemicalEntityMap[static_cast(flags)]; flags &= ~static_cast(Enums::ChemicalEntity::CROSS_LINKER); // qDebug() << "flags:" << flags // << chemicalEntityMap[static_cast(flags)]; local_calc_options.setMonomerEntities( static_cast(flags)); // flags = static_cast(local_calc_options.getPolymerEntities()); // qDebug() << "flags:" << flags // << chemicalEntityMap[static_cast(flags)]; if(!Polymer::accountMasses( mcsp_polymer.get(), local_calc_options, m_mono, m_avg)) { qCritical() << "Failed accounting masses of Polymer."; return false; } qDebug() << "20250522 After the enclosing polymer accounted masses (prior to " "cross-links), mono mass is:" << m_mono << "avg mass is:" << m_avg; // At this point, we have added the mass of each constituent // oligomer's residual chain (with or without end caps, depending // on the configuration). Let's deal with the cross-links, if // so is required. if(static_cast(calc_options.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::CROSS_LINKER)) { qDebug() << "As per configuration, now accounting " "the cross-links."; for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) { qDebug() << "Now iterating in one of the cross-links of the Oligomer:" << cross_link_sp->toString(); qDebug() << "Checking if Index ranges:" << local_calc_options.getIndexRangeCollectionRef().indicesAsText() << "fully encompass the cross-link."; // Only account the cross-link if it is entirely encompassed // by the index range. std::size_t in_count = 0; std::size_t out_count = 0; Enums::CrossLinkEncompassed cross_link_encompassed = cross_link_sp->isEncompassedByIndexRangeCollection( local_calc_options.getIndexRangeCollectionCstRef(), in_count, out_count); if(cross_link_encompassed == Enums::CrossLinkEncompassed::FULLY) { cross_link_sp->accountMasses(m_mono, m_avg); qDebug() << "After accounting fully encompassed cross-link:" << cross_link_sp->getCrossLinkerCstSPtr()->getName() << "mono mass is:" << m_mono << "avg mass is:" << m_avg; } } } // If the ionizer is valid use it. if(ionizer.isValid()) { if(ionizer.ionize(m_mono, m_avg) == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to ionize the Oligomer."; return false; } } qDebug() << "Coming out from the calculateMasses function:" << "mono mass is:" << m_mono << "avg mass is:" << m_avg; return true; } /*! \brief Returns the size of this Oligomer. The size is computed by adding the length of all the regions of the enclosing Polymer as documented in the Coordinates instances in the member coordinateList. */ std::size_t Oligomer::size() { std::size_t sum = 0; foreach(const IndexRange *item, m_calcOptions.getIndexRangeCollectionCstRef().getRangesCstRef()) sum += (item->m_stop - item->m_start + 1); return sum; } /*! \brief Returns true if this Oligomer spans at least one region of the enclosing polymer that contains a Monomer at \a index, false otherwise. */ bool Oligomer::encompasses(std::size_t index) const { if(mcsp_polymer == nullptr) { qCritical() << "The polymer is not available."; return false; } if(index >= mcsp_polymer->getSequenceCstRef().size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_calcOptions.getIndexRangeCollectionCstRef().encompassIndex(index); } /*! \brief Returns true if this Oligomer spans at least one region of the enclosing Polymer that contains the Monomer \a monomer_crp, false otherwise. The search is performed by comparing pointers, thus the Monomer to be search \e is \a monomer_crp. */ bool Oligomer::encompasses(MonomerCstRPtr monomer_crp) const { bool ok = false; std::size_t index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_crp, ok); return encompasses(index); } /*! \brief Returns true if this Oligomer spans at least one region of the enclosing Polymer that contains the Monomer \a monomer_sp, false otherwise. The search is performed by comparing pointers, thus the Monomer to be search \e is \a monomer_sp. */ bool Oligomer::encompasses(MonomerSPtr monomer_sp) const { bool ok = false; std::size_t index = mcsp_polymer->getSequenceCstRef().monomerIndex(monomer_sp, ok); return encompasses(index); } /*! \brief Returns a string documenting this Oligomer instance. */ QString Oligomer::toString() const { QString text = QString( "name: %1 - desc.: %2 - index ranges: %3 - mono: %4 - avg: " "%5 - stored formula :%6 - ionizer: %7 - calc. elem. comp. : %8\n") .arg(m_name) .arg(m_description) .arg(m_calcOptions.getIndexRangeCollectionCstRef().positionsAsText()) .arg(m_mono) .arg(m_avg) .arg(m_formula.getActionFormula()) .arg(m_ionizer.toString()) .arg(elementalComposition()); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/OligomerCollection.cpp000664 001750 001750 00000034500 15100504560 026562 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// libmass includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/OligomerCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::OligomerCollection \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile OligomerCollection.hpp \brief The OligomerCollection class provides abstractions to work with Oligomer instances that have been produced in specific contexts, like Polymer cleavages or Oligomer fragmentations. */ /*! \variable MsXpS::libXpertMassCore::OligomerCollection::mcsp_polymer \brief The \l Polymer instance about which this OligomerCollection is about. */ /*! \variable MsXpS::libXpertMassCore::OligomerCollection::m_name \brief The name of the OligomerCollection. */ /*! \variable MsXpS::libXpertMassCore::OligomerCollection::m_comment \brief A comment associated to the OligomerCollection. */ /*! \variable MsXpS::libXpertMassCore::OligomerCollection::m_massType \brief The type of mass that is dealt with in a number of calculations. */ /*! \variable MsXpS::libXpertMassCore::OligomerCollection::m_oligomers \brief The container of Oligomer instances in this OligomerCollection. */ /*! \typedef MsXpS::libXpertMassCore::OligomerCollectionSPtr \relates OligomerCollection Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::OligomerCollectionCstSPtr \relates OligomerCollection Synonym for std::shared_ptr. */ /*! \brief Constructs an OligomerCollection instance with a number of parameters. \list \li \a name The name of the OligomerCollection \li \a polymer_cqsp The Polymer about which this OligomerCollection is about \li \a mass_type The type of mass that is dealt with in a number of calculations \endlist */ OligomerCollection::OligomerCollection(const QString &name, PolymerCstQSPtr polymer_cqsp, Enums::MassType mass_type) : m_name(name), mcsp_polymer(polymer_cqsp), m_massType(mass_type) { } /*! \brief Constructs an OligomerCollection instance as a copy of \a other. The Oligomer instances in the member container are instantiated anew (deep copy). */ OligomerCollection::OligomerCollection(const OligomerCollection &other) : QObject(), m_name(other.m_name), m_comment(other.m_comment), mcsp_polymer(other.mcsp_polymer), m_massType(other.m_massType) { for(const OligomerSPtr &oligomer_sp : other.m_oligomers) m_oligomers.emplace_back(std::make_shared(*oligomer_sp.get())); } /*! \brief Destructs the OligomerCollection. */ OligomerCollection::~OligomerCollection() { m_oligomers.clear(); } /*! \brief Sets the the polymer to \a polymer_cqsp. */ void OligomerCollection::setPolymer(PolymerCstQSPtr polymer_cqsp) { mcsp_polymer = polymer_cqsp; } /*! \brief Returns the polymer. */ const PolymerCstQSPtr OligomerCollection::getPolymer() const { return mcsp_polymer; } /*! \brief Sets the \a name. */ void OligomerCollection::setName(const QString &name) { if(!name.isEmpty()) m_name = name; } /*! \brief Returns the name. */ const QString & OligomerCollection::getName() const { return m_name; } /*! \brief Sets the \a comment. */ void OligomerCollection::setComment(const QString &comment) { if(!comment.isEmpty()) m_comment = comment; } /*! \brief Returns the comment. */ const QString & OligomerCollection::getComment() const { return m_comment; } /*! \brief Returns a const reference to the Oligomer container. */ const std::vector & OligomerCollection::getOligomersCstRef() const { return m_oligomers; } /*! \brief Returns a reference to the Oligomer container. */ std::vector & OligomerCollection::getOligomersRef() { return m_oligomers; } /*! \brief Sets the type of mass \a mass_type to be dealt with by default. */ void OligomerCollection::setMassType(Enums::MassType mass_type) { m_massType = mass_type; if(m_massType == Enums::MassType::BOTH) qFatalStream() << "Programming error. The mass type cannot be Enums::MassType::BOTH."; } /*! \brief Returns the type of mass to be dealt with by default. */ Enums::MassType OligomerCollection::getMassType() const { return m_massType; } /*! \brief Returns the Oligomer instance that encompasses a Monomer at \a monomer_index. This function starts searching for Oligomer instances in the member Oligomer container at \a oligomer_index. If \a oligomer_index is out of bounds, nullptr is returned. When an Oligomer is found, its index in the member Oligomer container is set to \a oligomer_index and its pointer is returned. If no Oligomer is found, returns nullptr; */ OligomerSPtr OligomerCollection::findOligomerEncompassing(std::size_t monomer_index, std::size_t &oligomer_index) { // Is there any oligomer that emcompasses the index ? If so return // its pointer. Start searching in the list at index oligomer_index. if(oligomer_index >= m_oligomers.size()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_oligomers.cbegin() + oligomer_index; std::vector::const_iterator the_end_iterator_cst = m_oligomers.cend(); std::vector::const_iterator the_iterator_cst = m_oligomers.cbegin() + oligomer_index; while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->encompasses(monomer_index)) { oligomer_index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Returns the Oligomer instance that encompasses Monomer \a monomer_csp. This function starts searching for Oligomer instances in the member Oligomer container at \a oligomer_index. If \a oligomer_index is out of bounds, nullptr is returned. When an Oligomer is found, its index in the member Oligomer container is set to \a oligomer_index and its pointer is returned. If no Oligomer is found, returns nullptr; */ OligomerSPtr OligomerCollection::findOligomerEncompassing(MonomerCstSPtr monomer_csp, std::size_t &oligomer_index) { // Is there any oligomer that emcompasses the monomer_csp ? If so return // its pointer. Start searching in the list at index oligomer_index. if(monomer_csp == nullptr || monomer_csp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; if(oligomer_index >= m_oligomers.size()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_oligomers.cbegin() + oligomer_index; std::vector::const_iterator the_end_iterator_cst = m_oligomers.cend(); std::vector::const_iterator the_iterator_cst = m_oligomers.cbegin() + oligomer_index; while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->encompasses(monomer_csp.get())) { oligomer_index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Returns the Oligomer instance that encompasses Monomer \a monomer_sp. This function starts searching for Oligomer instances in the member Oligomer container at \a oligomer_index. If \a oligomer_index is out of bounds, nullptr is returned. When an Oligomer is found, its index in the member Oligomer container is set to \a oligomer_index and its pointer is returned. If no Oligomer is found, returns nullptr; */ OligomerSPtr OligomerCollection::findOligomerEncompassing(MonomerSPtr monomer_sp, std::size_t &oligomer_index) { // Is there any oligomer that emcompasses the monomer_sp ? If so return // its pointer. Start searching in the list at index oligomer_index. if(monomer_sp == nullptr || monomer_sp.get() == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; if(oligomer_index >= m_oligomers.size()) return nullptr; std::vector::const_iterator the_begin_iterator_cst = m_oligomers.cbegin() + oligomer_index; std::vector::const_iterator the_end_iterator_cst = m_oligomers.cend(); std::vector::const_iterator the_iterator_cst = m_oligomers.cbegin() + oligomer_index; while(the_iterator_cst != the_end_iterator_cst) { if((*the_iterator_cst)->encompasses(monomer_sp)) { oligomer_index = std::distance(the_begin_iterator_cst, the_iterator_cst); return *the_iterator_cst; } ++the_iterator_cst; } return nullptr; } /*! \brief Returns a string in which all the Oligomer instances present in the member container are represented as text. The masses of the Oligomer instances are reported for the \a mass_type. If \a mass_type is Enums::MassType::BOTH, the the member mass type is used. */ QString OligomerCollection::allOligomerMassesToString(Enums::MassType mass_type) { QString text; if(mass_type == Enums::MassType::BOTH && m_massType == Enums::MassType::BOTH) return text; Enums::MassType local_mass_type; if(mass_type == Enums::MassType::BOTH) local_mass_type = m_massType; else local_mass_type = mass_type; for(const OligomerSPtr &oligomer_sp : m_oligomers) { text += QString("%1\n").arg( oligomer_sp->getMass(local_mass_type), 0, 'f', OLIGOMER_DEC_PLACES); } return text; } /*! \brief Returns true if the monoisotopic mass of \a o1_sp is smaller than that of \a o2_sp, else returns false. */ bool OligomerCollection::monoMassCompare(const OligomerSPtr o1_sp, const OligomerSPtr o2_sp) { // qDebug() << "o1_sp is:" << o1_sp.get() << "o2_sp is:" << o2_sp.get(); // std:sort compare function: // Binary function that accepts two elements in the range as arguments, and // returns a value convertible to bool. The value returned indicates whether // the element passed as first argument is considered to go before the second // in the specific strict weak ordering it defines. return o1_sp->getMass(Enums::MassType::MONO) < o2_sp->getMass(Enums::MassType::MONO); } /*! \brief Returns true if the average mass of \a o1_sp is smaller than that of \a o2_sp, else returns false. */ bool OligomerCollection::avgMassCompare(const OligomerSPtr o1_sp, const OligomerSPtr o2_sp) { qDebug() << "o1_sp is:" << o1_sp.get() << "o2_sp is:" << o2_sp.get(); return o1_sp->getMass(Enums::MassType::AVG) < o2_sp->getMass(Enums::MassType::AVG); } /*! \brief Sorts the Oligomer instances in the member container according to ascending masses of the default member Enums::MassType. */ void OligomerCollection::sortAscending() { // We only can sort if this instance knows which mass type it should // handle, and that cannot be anything other than Enums::MassType::MASS_MONO // or MASS_AVG. if(m_massType == Enums::MassType::BOTH) { qDebug() << "Could not sort the oligomer list: " "m_massType cannot be Enums::MassType::BOTH."; return; } if(m_massType == Enums::MassType::MONO) std::sort(m_oligomers.begin(), m_oligomers.end(), OligomerCollection::monoMassCompare); else std::sort(m_oligomers.begin(), m_oligomers.end(), OligomerCollection::avgMassCompare); } /*! \brief Returns the size of this OligomerCollection as the count of Oligomer instances in the member container. */ std::size_t OligomerCollection::size() const { return m_oligomers.size(); } /*! \brief Returns a text string representing this OligomerCollection's Oligomer instances. \sa Oligomer::toString */ QString OligomerCollection::toString() const { QString text; for(OligomerSPtr oligomer_sp : m_oligomers) text += oligomer_sp->toString(); return text; } /*! \brief Clears the member Oligomer container. */ void OligomerCollection::clear() { m_oligomers.clear(); } /*! Assigns \a other to this instance. */ OligomerCollection & OligomerCollection::operator=(const OligomerCollection &other) { if(this == &other) return *this; m_name = other.m_name; m_comment = other.m_comment; mcsp_polymer = other.mcsp_polymer; m_massType = other.m_massType; m_oligomers.assign(other.m_oligomers.begin(), other.m_oligomers.end()); return *this; } /*! \brief Returns true if this OligomerCollection instance and \a other are identical, false otherwise. */ bool OligomerCollection::operator==(const OligomerCollection &other) const { if(this == &other) return true; if(m_name != other.m_name || m_comment != other.m_comment || mcsp_polymer != other.mcsp_polymer || m_massType != other.m_massType || m_oligomers != other.m_oligomers) return false; return true; } /*! \brief Returns true if this OligomerCollection instance and \a other differ, false otherwise. Returns the negated value returned from \l{operator==()}. */ bool OligomerCollection::operator!=(const OligomerCollection &other) const { return !operator==(other); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/OligomerPair.cpp000664 001750 001750 00000014517 15100504560 025370 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/OligomerPair.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::OligomerPair \inmodule libXpertMassCore \ingroup XpertMassCoreMassCalculations \inheaderfile OligomerPair.hpp \brief The OligomerPair class provides abstractions to work with a pair of Oligomer instances. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::m_name \brief The name of the OligomerPair. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::msp_first \brief The first Oligomer of the pair. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::msp_second \brief The second Oligomer of the pair. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::m_massType \brief The mass type that is to be handled. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::m_error \brief The error on the mass values. */ /*! \variable MsXpS::libXpertMassCore::OligomerPair::m_isMatching \brief Tells if the OligomerPair is matching. The concept of matching depends on the use case for this class. */ /*! \typedef MsXpS::libXpertMassCore::OligomerPairSPtr \relates OligomerPair Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::OligomerPairCstSPtr \relates OligomerPair Synonym for std::shared_ptr. */ /*! \brief Constructs an OligomerPair. The OligomerPair instance is constructed with these arguments: \list \li \a name The name of this OligomerPair \li \a first_csp The first Oligomer \li \a first_csp The second Oligomer \li \a mass_type Type of mass that is being dealt with \li \a error The error \li \a isMatching If there is a match \endlist \a first_csp and \a second_csp cannot be nullptr. */ OligomerPair::OligomerPair(const QString &name, const OligomerSPtr first_csp, const OligomerSPtr second_csp, Enums::MassType mass_type, double error, bool isMatching) : m_name(name), msp_first(first_csp), msp_second(second_csp), m_massType(mass_type), m_error(error), m_isMatching(isMatching) { if(msp_first == nullptr || msp_second == nullptr) qFatalStream() << "Programming error. Pointer(s) cannot be nullptr."; } /*! \brief Constructs the OligomerPair as a copy of \a other. */ OligomerPair::OligomerPair(const OligomerPair &other) : PropListHolder(other), m_name(other.m_name), msp_first(other.msp_first), msp_second(other.msp_second), m_massType(other.m_massType), m_error(other.m_error), m_isMatching(other.m_isMatching) { if(msp_first == nullptr || msp_second == nullptr) qFatalStream() << "Programming error. Pointer(s) cannot be nullptr."; } /*! \brief Desstructs the OligomerPair. */ OligomerPair::~OligomerPair() { } /*! \brief Returns the name. */ QString OligomerPair::getName() { return m_name; } /*! \brief Returns the first Oligomer. */ const OligomerSPtr OligomerPair::getFirst() const { return msp_first; } /*! \brief Returns the second Oligomer. */ const OligomerSPtr OligomerPair::getSecond() const { return msp_second; } /*! \brief Sets the \a mass_type. */ void OligomerPair::setMassType(Enums::MassType mass_type) { m_massType = mass_type; } /*! \brief Returns the mass type. */ Enums::MassType OligomerPair::getMassType() const { return m_massType; } /*! \brief Sets the \a error. */ void OligomerPair::setError(double error) { m_error = error; } /*! \brief Returns the error. */ double OligomerPair::getError() const { return m_error; } /*! \brief Sets the matching to \a is_matching. */ void OligomerPair::setMatching(bool is_matching) { m_isMatching = is_matching; } /*! \brief Returns the matching. */ bool OligomerPair::isMatching() const { return m_isMatching; } /*! \brief Returns the mass of the first Oligomer of the type set in the member Enums::MassType. */ double OligomerPair::getFirstMass() { return msp_first->getMass(m_massType); } /*! \brief Returns the mass of the second Oligomer of the type set in the member Enums::MassType. */ double OligomerPair::getSecondMass() { return msp_second->getMass(m_massType); } /*! \brief Returns the charge of the first Oligomer as calculated in the Oligomer's Ionizer member. \sa Ionizer */ int OligomerPair::firstCharge() { return msp_first->getIonizerCstRef().charge(); } /*! \brief Returns the charge of the second Oligomer as calculated in the Oligomer's Ionizer member. \sa Ionizer */ int OligomerPair::secondCharge() { return msp_second->getIonizerCstRef().charge(); } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this Oligomer instance. */ OligomerPair & OligomerPair::operator=(const OligomerPair &other) { if(this == &other) return *this; PropListHolder::operator=(other); m_name = other.m_name; msp_first = other.msp_first; msp_second = other.msp_second; m_massType = other.m_massType; m_error = other.m_error; m_isMatching = other.m_isMatching; return *this; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/PkaPhPi.cpp000664 001750 001750 00000106755 15100504560 024301 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/PkaPhPi.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::PkaPhPi \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile PkaPhPi.hpp \brief The PkaPhPi class provides a model for specifying the acido-basic properties of a chemical entity. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::mcsp_polChemDef \brief The polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_monomers \brief The container of \l Monomer instances as read from the pka_ph_pi.xml file. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_modifs \brief The container of \l Modif instances as read from the pka_ph_pi.xml file. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_ph \brief The pH of the environment. This pH value is required to compute the number of charges of a given chemical entity (a \l Polymer) sequence, for example. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_pi \brief The pI of the chemical entity. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::mcsp_polymer \brief The polymer of which the acidobasic properties are computed. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_calcOptions \brief The \l CalcOptions that configure the way the computations are to be carried out. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_positiveCharges \brief The count of positive charges. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPi::m_negativeCharges \brief The count of negative charges. */ /*! \brief Constructs a PkaPhPi instance with a number of parameters. \list \li \a pol_chem_def_csp: The polymer chemistry definition \li \a polymer_cqsp: The polymer within the context of which the calculations are performed. \li \a calc_options: The options driving the calculations. \endlist */ PkaPhPi::PkaPhPi(PolChemDefCstSPtr pol_chem_def_csp, PolymerCstQSPtr polymer_cqsp, const CalcOptions &calc_options) : mcsp_polChemDef(pol_chem_def_csp), mcsp_polymer(polymer_cqsp), m_calcOptions(calc_options) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Creating PkaPhPi instance without polymer chemistry definition."; if(polymer_cqsp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; } /*! \brief Destructs this PkaPhPi instance. */ PkaPhPi::~PkaPhPi() { } /*! \brief Sets the PolChemDef to \a pol_chem_def_csp. */ void PkaPhPi::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qCritical() << "Setting an Creating PkaPhPi instance without polymer " "chemistry definition."; } /*! \brief Returns the PolChemDef. */ PolChemDefCstSPtr PkaPhPi::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } /*! \brief Moves the Monomer instances from \a monomers to the member container. */ void PkaPhPi::setMonomers(std::vector &&monomers) { m_monomers.clear(); m_monomers = std::move(monomers); } /*! \brief Moves the Monomer instances from \a monomers to the member container. */ void PkaPhPi::setMonomers(std::vector &&monomers) { // We want the pointers to be to const Monomer. m_monomers.clear(); m_monomers.reserve(monomers.size()); std::transform(std::make_move_iterator(monomers.begin()), std::make_move_iterator(monomers.end()), std::back_inserter(m_monomers), [](std::shared_ptr &&monomer_sp) { return std::const_pointer_cast( std::move(monomer_sp)); }); monomers.clear(); } /*! \brief Copies the Monomer instances in \a monomers to the member container. This is a shallow copy with only the pointers being copied. */ void PkaPhPi::setMonomers(std::vector &monomers) { m_monomers = monomers; } /*! \brief Returns a const reference to the container of Monomer instances. */ const std::vector & PkaPhPi::getMonomersCstRef() const { return m_monomers; } /*! \brief Moves the Modif instances from \a modifs to the member container. */ void PkaPhPi::setModifs(std::vector &&modifs) { m_modifs.clear(); m_modifs = std::move(modifs); modifs.clear(); } /*! \brief Moves the Modif instances from \a modifs to the member container. */ void PkaPhPi::setModifs(std::vector &&modifs) { // We want the pointers to be to const Modif. m_modifs.clear(); m_modifs.reserve(modifs.size()); std::transform(std::make_move_iterator(modifs.begin()), std::make_move_iterator(modifs.end()), std::back_inserter(m_modifs), [](std::shared_ptr &&modif_sp) { return std::const_pointer_cast( std::move(modif_sp)); }); modifs.clear(); } /*! \brief Copies the Modif instances in \a modifs to the member container. This is a shallow copy with only the pointers being copied. */ void PkaPhPi::setModifs(std::vector &modifs) { m_modifs = modifs; } /*! \brief Returns a const reference to the container of Modif instances. */ const std::vector & PkaPhPi::getModifs() const { return m_modifs; } /*! \brief Sets the pH to \a ph. */ void PkaPhPi::setPh(double ph) { Q_ASSERT(ph > 0 && ph < 14); m_ph = ph; } /*! \brief Returns the pH. */ double PkaPhPi::ph() { return m_ph; } /*! \brief Returns the pI. */ double PkaPhPi::pi() { return m_pi; } /*! \brief Returns the positive charges. */ double PkaPhPi::positiveCharges() { return m_positiveCharges; } /*! \brief Returns the negative charges. */ double PkaPhPi::negativeCharges() { return m_negativeCharges; } /*! \brief Sets the calculation options to \a calc_options. */ void PkaPhPi::setCalcOptions(const libXpertMassCore::CalcOptions &calc_options) { m_calcOptions.initialize(calc_options); } /*! \brief Calculates the charges (positive and negative). The general scheme is : Get the list of the iter_index_range of the different \l Polymer region selections. For each first monomer and end monomer of a given region selection, check if the the region is an oligomer or a residual chain (m_selectionType of libXpertMassCore::CalcOptions); act accordingly. Also, check for each selection region if it encompasses the polymer left/right end. If the left/right end modifications are to be taken into account, act accordingly. The positive and negative charges are stored in the member \l m_positiveCharges and \l m_negativeCharges variables. Returns the count of chemical groups that have been processed. \sa calculatePi() */ int PkaPhPi::calculateCharges() { int processedChemicalGroups = 0; m_positiveCharges = 0; m_negativeCharges = 0; // We of course need monomers ! Instead, we may not need modifs. if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) return -1; std::size_t polymerSize = mcsp_polymer->size(); const IndexRangeCollection &index_range_collection = m_calcOptions.getIndexRangeCollectionCstRef(); for(qsizetype iter = 0; iter < index_range_collection.size(); ++iter) { const IndexRange &iter_index_range = index_range_collection.getRangeCstRefAt(iter); qsizetype start_index = iter_index_range.m_start; qsizetype stop_index = iter_index_range.m_stop; bool is_left_most_sequence_range = index_range_collection.isLeftMostIndexRange(iter_index_range); bool is_right_most_sequence_range = index_range_collection.isRightMostIndexRange(iter_index_range); for(qsizetype jter = start_index; jter < stop_index + 1; ++jter) { MonomerSPtr monomer_csp = mcsp_polymer->getSequenceCstRef().getMonomerCstSPtrAt(jter); // Find a monomer by the same code in our list of monomers // that have been fed with chemical group data. Note that // all the monomers in a given sequence must not // necessarily have a counterpart in the local list of // monoemers. For example, there might be cases in which a // given monomer might not bring any charge whatsoever. QString code = monomer_csp->getCode(); std::vector::const_iterator monomer_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [code](const MonomerCstSPtr &monomer_csp) { return monomer_csp->getCode() == code; }); if(monomer_iterator_cst == m_monomers.cend()) continue; // A monomer can have multiple such "CHEMICAL_GROUP" // properties. Indeed, for example for proteins, a monomer // might have three such chemical groups(and thus three // Prop objects): one for the alpha NH2, one for the // alpha COOH and one for a residual chain chemical group, like // epsilon NH2 for lysine. for(int kter = 0; kter < (*monomer_iterator_cst)->propList().size(); ++kter) { Prop *prop = (*monomer_iterator_cst)->propList().at(kter); if(prop->name() != "CHEMICAL_GROUP") continue; // qDebug() << __FILE__ << __LINE__ // << "Monomer has property CHEMICAL_GROUP..."; // Get the chemical group out of the property. ChemicalGroup *chemicalGroup = static_cast(prop->data()); if(static_cast(chemicalGroup->getPolRule()) & static_cast(Enums::ChemicalGroupTrapped::LEFT)) { // qDebug() << __FILE__ << __LINE__ // << "... that is CHEMGROUP_LEFT_TRAPPED"; // The chemical group we are dealing with is trapped // when the monomer is polymerized on the left end, that // is if the monomer is not the left end monomer of the // sequence being analyzed. // Thus we only can take it into account if one of // two conditions are met: // 1. The monomer is the left end monomer of the // whole polymer sequence. // 2. The monomer is the left end monomer of the // region selection AND the selection type is // oligomers(thus it does not get polymerized to // the previous selection region). if(jter > 0) { // Clearly we are not dealing with the left // end of the polymer, so check if we have to // account for this chemical group or not. if(!is_left_most_sequence_range) { // The current libXpertMassCore::Coordinates is not the // left-most libXpertMassCore::Coordinates in the // libXpertMassCore::CoordinateList, thus we cannot // consider it to be the "left end // iter_index_range" of the // libXpertMassCore::CoordinateList. Just continue without // exploring any more. continue; } if(jter == start_index) { // The current monomer is the first // monomer of libXpertMassCore::Coordinates. We only take // into account the chemical group if each // libXpertMassCore::Coordinates is to be considered an // oligomer. if(m_calcOptions.getSelectionType() != Enums::SelectionType::OLIGOMERS) continue; } } } if(static_cast(chemicalGroup->getPolRule()) & static_cast(Enums::ChemicalGroupTrapped::RIGHT)) { // qDebug() << __FILE__ << __LINE__ // << "... that is CHEMGROUP_RIGHT_TRAPPED"; // See explanations above. if(jter < (qsizetype) polymerSize - 1) { // Clearly, we are not dealing with the right // end of the polymer. if(!is_right_most_sequence_range) { // The current libXpertMassCore::Coordinates is not the // right-most libXpertMassCore::Coordinates of the // libXpertMassCore::CoordinateList, thus we cannot // consider it to be the "right end // iter_index_range" of the // libXpertMassCore::CoordinateList. Just continue without // exploring anymore. continue; } if(jter == stop_index) { // The current monomer is the last monomer // of libXpertMassCore::Coordinates. We only take into // account the chemical group if each // libXpertMassCore::Coordinates is to be considered an // oligomer(and not a residual chain). if(m_calcOptions.getSelectionType() != Enums::SelectionType::OLIGOMERS) continue; } } } if(iter == 0 && static_cast(m_calcOptions.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::LEFT_END_MODIF)) { // We are iterating in the monomer that is at the // beginning of the polymer sequence. We should // test if the chemical group we are dealing with // right now has a special rule for the left end // of the polymer sequence region. int ret = accountPolymerEndModif( Enums::ChemicalEntity::LEFT_END_MODIF, *chemicalGroup); if(ret >= 0) { // qDebug() << __FILE__ << __LINE__ // << "Accounted for left end modif."; processedChemicalGroups += ret; continue; } } if(iter == (qsizetype) polymerSize - 1 && static_cast(m_calcOptions.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::RIGHT_END_MODIF)) { int ret = accountPolymerEndModif( Enums::ChemicalEntity::RIGHT_END_MODIF, *chemicalGroup); if(ret >= 0) { // qDebug() << __FILE__ << __LINE__ // << "Accounted for right end modif."; processedChemicalGroups += ret; continue; } } if(static_cast(m_calcOptions.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::MODIF) && (*monomer_iterator_cst)->isModified()) { int ret = accountMonomerModif(*(*monomer_iterator_cst), *chemicalGroup); if(ret >= 0) { // qDebug() << __FILE__ << __LINE__ // << "Accounted for monomer modif."; processedChemicalGroups += ret; continue; } } double charge = calculateChargeRatio( chemicalGroup->getPka(), chemicalGroup->isAcidCharged()); // qDebug() << __FILE__ << __LINE__ // << "Charge:" << charge; if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; // qDebug() << __FILE__ << __LINE__ // << "Pos =" << m_positiveCharges // << "Neg = " << m_negativeCharges; ++processedChemicalGroups; } // End of // for (int kter = 0; kter < monomer->propList().size(); ++kter) // qDebug() << __FILE__ << __LINE__ // << "End dealing with Monomer:" << // seqMonomer->name() // << "position:" << jter + 1; } // End of // for (int jter = start_index; jter < stop_index + 1; ++jter) // qDebug() << __FILE__ << __LINE__ // << "End dealing with libXpertMassCore::Coordinates"; } // End of // for (int iter = 0; iter < index_range_collection.size(); ++iter) // We have finished processing all the libXpertMassCore::Coordinates in the list. return processedChemicalGroups; } /*! \brief Calculates the isoelectric point. The isoelectric point is the pH at which a given analyte will have a net charge of 0, that is, when the count of negative charges will be equal to the count of positive charges. The pI will be stored in the \l m_pi member variable. Returns the count of chemical groups that have been processed. \sa calculateCharges() */ int PkaPhPi::calculatePi() { int processedChemicalGroups = 0; int iteration = 0; double netCharge = 0; double firstCharge = 0; double thirdCharge = 0; // We of course need monomers ! Instead, we may not need modifs. if(!m_monomers.size()) { m_pi = 0; m_ph = 0; return -1; } m_ph = 0; while(true) { // qDebug() << "Current pH being tested:" << m_ph; processedChemicalGroups = calculateCharges(); if(processedChemicalGroups == -1) { qDebug() << "Failed to calculate net charge for pH" << m_ph; m_pi = 0; m_ph = 0; return -1; } netCharge = m_positiveCharges + m_negativeCharges; // Note that if the 0.01 tested_ph step is enough to switch the // net charge from one excess value to another excess value in // the opposite direction, we'll enter an infinite loop. // // The evidence for such loop is that every other two measures, // the net charge of the polymer sequence will be the same. // // Here we test this so that we can break the loop. ++iteration; if(iteration == 1) { firstCharge = netCharge; } else if(iteration == 3) { thirdCharge = netCharge; if(firstCharge == thirdCharge) break; iteration = 0; firstCharge = netCharge; } // At this point we have to test the net charge: if(netCharge >= -0.1 && netCharge <= 0.1) { // qDebug() << "Breaking loop with netCharge:" << netCharge; break; } if(netCharge > 0) { // The test ph is too low. m_ph += 0.01; // qDebug() << "Set new pH m_ph += 0.01:" << m_ph // << "netCharge:" << netCharge; continue; } if(netCharge < 0) { // The test ph is too high. m_ph -= 0.01; // qDebug() << "Set new pH m_ph -= 0.01:" << m_ph // << "netCharge:" << netCharge; continue; } } // End of // while(true) // At this point m_pi is m_ph. m_pi = m_ph; // qDebug() << "pi is:" << m_pi; return processedChemicalGroups; } /*! \brief Returns the ratio between the charged and the uncharged forms of the chemical entity using \a pka and \a is_acid_charged. If the charge is negative, the returned ratio is negative, positive otherwise. The charged and uncharged species are the AH an A- species of the acido-basic theory. The calculation is based on the use of the m_ph member variable value. */ double PkaPhPi::calculateChargeRatio(double pka, bool is_acid_charged) { double aOverAh = 0; if(pka < 0) return 0; if(pka > 14) return 0; if(m_ph < 0) return 0; if(m_ph > 14) return 0; // Example with pKa = 4.25(Glu) ; pH = 4.16, thus we are more // acidic than pKa, we expect AH to be greater than A by a small // margin. aOverAh = (double)pow(10, (m_ph - pka)); // aOverAh = 0.81283051616409951 (confirmed manually) if(aOverAh < 1) { /* The solution contains more acid forms(AH) than basic forms (A). */ if(is_acid_charged) return (1 - aOverAh); else // The acid is not charged, that is, it is a COOH. // AH = 1 - A // A = aOverAh.AH // A = aOverAh.(1-A) // A = aOverAh - aOverAh.A // A(1+aOverAh) = aOverAh // A = aOverAh /(1+aOverAh), for us this is // A = 0.81283 / 1.81283 = 0.448 // And not - aOverAh, that is - aOverAh. // Below seems faulty(20071204) // return(- aOverAh); // Tentative correction(20071204) return (-(aOverAh / (1 + aOverAh))); } else if(aOverAh > 1) { /* The solution contains more basic forms(A) than acid forms (AH). */ if(is_acid_charged) return (1 / aOverAh); else return (-(1 - (1 / aOverAh))); } else if(aOverAh == 1) { /* The solution contains as many acid forms(AH) as basic forms (H). */ if(is_acid_charged) return (aOverAh / 2); else return (-aOverAh / 2); } else qFatal("Programming error."); return 0; } /*! \brief Accounts for the \a chemical_group according the the polymer chemical entities defined in \a polymer_chem_ent. A chemical group is described as follows: \code C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped \endcode Returns the count of rules that were accounted for, or -1 if none was. */ int PkaPhPi::accountPolymerEndModif(Enums::ChemicalEntity polymer_chem_ent, const ChemicalGroup &chemical_group) { QString modifName; ChemicalGroupRuleSPtr chemical_group_rule_sp = nullptr; int count = 0; // Get the name of the modification of the polymer (if any) and get // the rule dealing with that polymer modification (if any). std::size_t chem_group_rule_index; if(static_cast(polymer_chem_ent) & static_cast(Enums::ChemicalEntity::LEFT_END_MODIF)) { modifName = mcsp_polymer->getLeftEndModifCstRef().getName(); chemical_group_rule_sp = chemical_group.findRuleByEntityAndName( "LE_PLM_MODIF", modifName, chem_group_rule_index); } if(static_cast(polymer_chem_ent) & static_cast(Enums::ChemicalEntity::RIGHT_END_MODIF)) { modifName = mcsp_polymer->getRightEndModifCstRef().getName(); chemical_group_rule_sp = chemical_group.findRuleByEntityAndName( "RE_PLM_MODIF", modifName, chem_group_rule_index); } else qFatal("Programming error."); // The polymer might not be modified, and also the chemical group // passed as parameter might not contain any rule about any polymer // modification. In that case we just have nothing to do. if(modifName.isEmpty()) { if(chemical_group_rule_sp != nullptr) { double charge = calculateChargeRatio(chemical_group.getPka(), chemical_group.isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; return ++count; } else { // The polymer end was NOT modified and the chemical group // was NOT eligible for a polymer end modification. This // means that we do not have to process it, and we return -1 // so that the caller function knows we did not do anything // and that this chemical group should continue to undergo // analysis without skipping it. return -1; } } // End of // if (modifName.isEmpty()) if(chemical_group_rule_sp == nullptr) { // This chemical group was not "designed" to receive any polymer // end modification, so we have nothing to do with it and we // return -1 so that the caller function knows we did not do // anything and that this chemical group should continue to // undergo analysis without skipping it. return -1; } // At this point we know that the chemical group 'group' we are // analyzing is eligible for a polymer left end modification and // that it is indeed modified with a correcct modification. So we // have a rule for it. Let's continue the analysis. // Apparently the rule has data matching the ones we are looking // for. At this point we should now what action to take for this // group. if(chemical_group_rule_sp->getFate() == Enums::ChemicalGroupFate::LOST) { // We do not use the current chemical group 'group' because the // polymer end's modification has abolished it. } else if(chemical_group_rule_sp->getFate() == Enums::ChemicalGroupFate::PRESERVED) { double charge = calculateChargeRatio(chemical_group.getPka(), chemical_group.isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; return ++count; } else qFatal("Programming error."); // Whatever we should do with the left/right end monomer's chemgroup, // we should take into account the modification itself that might // have brought chemgroup(s) worth calculating their intrinsic // charges! // Find a modif object in the local list of modif objects, that has // the same name as the modification with which the left/right end // of the polymer is modified. We'll see what chemgroup(s) this // modification brings to the polymer sequence. std::vector::const_iterator modif_iterator_cst = std::find_if(m_modifs.cbegin(), m_modifs.cend(), [modifName](const ModifCstSPtr &modif_csp) { return modif_csp->getName() == modifName; }); if(modif_iterator_cst == m_modifs.cend()) { // qDebug() << __FILE__ << __LINE__ // << "Information: following modif not in local list:" // << modifName; return count; } for(int jter = 0; jter < (*modif_iterator_cst)->propList().size(); ++jter) { Prop *prop = (*modif_iterator_cst)->propList().at(jter); if(prop->name() != "CHEMICAL_GROUP") continue; // Get the chemical group out of the property. const ChemicalGroup *chemicalGroup = static_cast(prop->data()); double charge = calculateChargeRatio(chemicalGroup->getPka(), chemicalGroup->isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; ++count; } return count; } /*! \brief Accounts for the \a chemical_group in the context of the \a monomer. A chemical group is described as follows: \code C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped \endcode Returns the count of rules that were accounted for, or -1 if none was. */ int PkaPhPi::accountMonomerModif(const Monomer &monomer, ChemicalGroup &chemical_group) { QString modifName; ChemicalGroupRuleSPtr rule_sp = nullptr; int count = 0; // For each modification in the monomer, make the accounting work. std::size_t chem_group_rule_index = 0; for(const ModifSPtr &modif_sp : monomer.getModifsCstRef()) { // Get the name of the modification of the monomer(if any) and get // the rule dealing with that monomer modification(if any). modifName = modif_sp->getName(); rule_sp = chemical_group.findRuleByEntityAndName( "MONOMER_MODIF", modifName, chem_group_rule_index); if(modifName.isEmpty()) { // The monomer does not seem to be modified. However, we still // have to make sure that the chemgroup that we were parsing is // actually a chemgroup suitable for a modif. If this chemgroup // was actually suitable for a monomer modif, but it is not // effectively modified, that means that we have to calculate // the charge for the non-modified chemgroup... if(rule_sp != nullptr) { double charge = calculateChargeRatio( chemical_group.getPka(), chemical_group.isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; return ++count; } else { // The current monomer was NOT modified, and the chemgroup // was NOT eligible for a monomer modification. This means // that we do not have to process it, and we return -1 so // that the caller function knows we did not do anything and // that this chemgroup should continue to undergo analysis // without skipping it. return -1; } } // End of // if (modifName.isEmpty()) if(rule_sp == nullptr) { // This chemgroup was not "designed" to receive any // modification, so we have nothing to do with it, and we return // -1 to let the caller know that its processing should be // continued in the caller's function space. return -1; } // At this point, we know that the chemgroup we are analyzing is // eligible for a modification and that we have a rule for it. Let's // continue the analysis: // Apparently, a rule object has member data matching the ones we // were looking for. At this point we should know what action to // take for this chemgroup. if(rule_sp->getFate() == Enums::ChemicalGroupFate::LOST) { // We do not use the current chemical group 'group' because the // monomer modification has abolished it. } else if(rule_sp->getFate() == Enums::ChemicalGroupFate::PRESERVED) { double charge = calculateChargeRatio(chemical_group.getPka(), chemical_group.isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; return ++count; } else qFatalStream() << "Programming error."; // Whatever we should do with this monomer's chemgroup, we should // take into account the modification itself that might have brought // chemgroup(s) worth calculating their intrinsic charges! // Find a modif object in the local list of modif objects, that has // the same name as the modification with which the monomer is // modified. We'll see what chemgroup(s) this modification brings to // the polymer sequence. std::vector::const_iterator modif_monomer_iterator_cst = std::find_if(m_modifs.cbegin(), m_modifs.cend(), [modifName](const ModifCstSPtr &modif_csp) { return modif_csp->getName() == modifName; }); if(modif_monomer_iterator_cst == m_modifs.cend()) { // qDebug() << __FILE__ << __LINE__ // << "Information: following modif not in local list:" // << modifName; return count; } for(int jter = 0; jter < (*modif_monomer_iterator_cst)->propList().size(); ++jter) { Prop *prop = (*modif_monomer_iterator_cst)->propList().at(jter); if(prop->name() != "CHEMICAL_GROUP") continue; // Get the chemical group out of the property. const ChemicalGroup *chemicalGroup = static_cast(prop->data()); double charge = calculateChargeRatio(chemicalGroup->getPka(), chemicalGroup->isAcidCharged()); if(charge < 0) m_negativeCharges += charge; else if(charge > 0) m_positiveCharges += charge; ++count; } } return count; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/PkaPhPiDataParser.cpp000664 001750 001750 00000033624 15100504560 026242 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/PkaPhPiDataParser.hpp" #include "MsXpS/libXpertMassCore/ChemicalGroup.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::PkaPhPiDataParser \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile PkaPhPiDataParser.hpp \brief The PkaPhPiDataParser class provides a file reader for the pKa, pH, pI data XML file. The format is the following: \code A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped [...] C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE \endcode */ /*! \variable MsXpS::libXpertMassCore::PkaPhPiDataParser::mcsp_polChemDef \brief The polymer chemistry definition context in which this PkaPhPiDataParser instance exists. */ /*! \variable MsXpS::libXpertMassCore::PkaPhPiDataParser::m_filePath \brief The path of the file that contains all the specifications for chemical groups and chemical rules. */ /*! \brief Constructs a PkaPhPiDataParser instance. \list \li \a pol_chem_def_csp: The polymer chemistry definition (cannot be nullptr). \li \a file_path: the specification file path. \endlist */ PkaPhPiDataParser::PkaPhPiDataParser(const PolChemDefCstSPtr &pol_chem_def_csp, const QString &file_path) : mcsp_polChemDef(pol_chem_def_csp), m_filePath(file_path) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "Programming error. Cannot be that pointer is nullptr."; } /*! \brief Destructs this PkaPhPiDataParser instance */ PkaPhPiDataParser::~PkaPhPiDataParser() { } /*! \brief Sets the \a file_path. */ void PkaPhPiDataParser::setFilePath(const QString &file_path) { m_filePath = file_path; } /*! \brief Returns the file path. */ const QString & PkaPhPiDataParser::filePath() { return m_filePath; } /*! \brief Parses the file and fills-in the \a monomers and the \a modifs containers. The two container hold specific Monomer and Modif instances as desribed in the loaded file. Returns true upon success, false otherwise. */ bool PkaPhPiDataParser::renderXmlFile(std::vector &monomers, std::vector &modifs) { // // // // A // // N-term NH2 // 9.6 // TRUE // left_trapped // // LE_PLM_MODIF // Acetylation // LOST // // // // C-term COOH // 2.35 // FALSE // right_trapped // // // [...] // // C // // N-term NH2 // 9.6 // TRUE // left_trapped // // LE_PLM_MODIF // Acetylation // LOST // // // // C-term COOH // 2.35 // FALSE // right_trapped // // // Lateral SH2 // 8.3 // FALSE // never_trapped // // // // // // Phosphorylation // // none_set // 1.2 // FALSE // // // none_set // 6.5 // FALSE // // // // // // The DTD stipulates that: // // // // // // QDomDocument doc("pkaPhPiData"); QDomElement element; QDomElement child; QDomElement indentedChild; QFile file(m_filePath); if(!file.open(QIODevice::ReadOnly)) return false; if(!doc.setContent(&file)) { file.close(); return false; } file.close(); element = doc.documentElement(); if(element.tagName() != "pkaphpidata") { qDebug() << __FILE__ << __LINE__ << "pKa-pH-pI data file is erroneous\n"; return false; } // The first child element must be . child = element.firstChildElement(); if(child.tagName() != "monomers") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; return false; } // Parse the elements. indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "monomer") return false; QDomElement superIndentedElement = indentedChild.firstChildElement(); if(superIndentedElement.tagName() != "code") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; return false; } MonomerSPtr monomer_sp = std::make_shared(mcsp_polChemDef, /*name*/ "", superIndentedElement.text(), /*formula string*/ "", 0.0, 0.0); // All the elements, if any. superIndentedElement = superIndentedElement.nextSiblingElement(); while(!superIndentedElement.isNull()) { if(superIndentedElement.tagName() != "mnmchemgroup") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; monomer_sp.reset(); return false; } ChemicalGroup *chemGroup = new ChemicalGroup("NOT_SET"); if(!chemGroup->renderXmlMnmElement(superIndentedElement)) { qCritical() << "The PkaPhPi data file could not be parsed: the " " element failed to render."; monomer_sp.reset(); delete chemGroup; return false; } ChemicalGroupProp *prop = new ChemicalGroupProp("CHEMICAL_GROUP", chemGroup); monomer_sp->appendProp(prop); superIndentedElement = superIndentedElement.nextSiblingElement(); } monomers.push_back(monomer_sp); indentedChild = indentedChild.nextSiblingElement(); } #if 0 qDebug() << __FILE__ << __LINE__ << "Debug output of all the monomers parsed:"; for (int iter = 0; iter < monomerList->size(); ++iter) { Monomer *monomer = monomerList->at(iter); qDebug() << __FILE__ << __LINE__ << "Monomer:" << monomer->name(); for(int jter = 0; jter < monomer->propList()->size(); ++jter) { Prop *prop = monomer->propList()->at(jter); if (prop->name() == "CHEMICAL_GROUP") { const ChemicalGroup *chemGroup = static_cast(prop->data()); qDebug() << __FILE__ << __LINE__ << "Chemical group:" << chemGroup->name() << chemGroup->pka(); } } } #endif // And now parse the elements, if any, this time, as // this element is not compulsory. child = child.nextSiblingElement(); if(child.isNull()) return true; if(child.tagName() != "modifs") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; return false; } // Parse the elements. indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "modif") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; return false; } QDomElement superIndentedElement = indentedChild.firstChildElement(); if(superIndentedElement.tagName() != "name") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; return false; } ModifSPtr modif_sp = std::make_shared( mcsp_polChemDef, superIndentedElement.text(), "H0"); // All the elements, if any. superIndentedElement = superIndentedElement.nextSiblingElement(); while(!superIndentedElement.isNull()) { if(superIndentedElement.tagName() != "mdfchemgroup") { qCritical() << "The PkaPhPi data file could not be parsed: the " " element was not found."; modif_sp.reset(); return false; } ChemicalGroup *chemGroup = new ChemicalGroup("NOT_SET"); if(!chemGroup->renderXmlMdfElement(superIndentedElement)) { qCritical() << "The PkaPhPi data file could not be parsed: the " " element failed to render."; modif_sp.reset(); delete chemGroup; return false; } ChemicalGroupProp *prop = new ChemicalGroupProp("CHEMICAL_GROUP", chemGroup); modif_sp->appendProp(prop); superIndentedElement = superIndentedElement.nextSiblingElement(); } modifs.push_back(modif_sp); indentedChild = indentedChild.nextSiblingElement(); } #if 0 qDebug() << __FILE__ << __LINE__ << "Debug output of all the modifs parsed:"; for (int iter = 0; iter < modifList->size(); ++iter) { Modif *modif = modifList->at(iter); // qDebug() << __FILE__ << __LINE__ // << "Modif:" << modif->name(); for(int jter = 0; jter < modif->propList()->size(); ++jter) { Prop *prop = modif->propList()->at(jter); if (prop->name() == "CHEMICAL_GROUP") { const ChemicalGroup *chemGroup = static_cast(prop->data()); qDebug() << __FILE__ << __LINE__ << "Chemical group:" << chemGroup->name() << chemGroup->pka(); } } } #endif return true; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/PolChemDef.cpp000664 001750 001750 00000226544 15100504560 024752 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ ////////////////////////////// Stdlib includes #include #include /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/Monomer.hpp" #include "MsXpS/libXpertMassCore/CrossLinker.hpp" #include "MsXpS/libXpertMassCore/CleavageAgent.hpp" #include "MsXpS/libXpertMassCore/FragmentationPathway.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" int polChemDefSPtrMetaTypeId = qRegisterMetaType("PolChemDefSPtr"); int polChemDefCstSPtrMetaTypeId = qRegisterMetaType( "PolChemDefCstSPtr"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::PolChemDef \inmodule libXpertMassCore \ingroup PolChemDef \inheaderfile PolChemDef.hpp \brief The PolChemDef class provides a complete set of chemical entities fully qualifying a polymer chemistry definition, like Proteins, Saccharides or Nucleic acids. The PolChemDef class provides a full set of chemical entity definitions (\l{Isotope}s, \l{Monomer}s, chemical \l{Modif}ications, \l{CrossLink}s), chemical reaction models (in the liquid or gas phase, like \l Polymer cleavage: \l CleavageAgent or \l Oligomer fragmentation: \l FragmentationPathway)\dots */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_name \brief The name of the polymer chemistry definition, like \e{protein-1-letter} or \e{nucac}, for example. This name is typically identical to both the name of the directory where all the data defining this \c PolChemDef is stored and the name of the XML file that contains the definition itself. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_xmlDataFilePath \brief The path to the XML data file that contains the description of this polymer chemistry definition. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_isotopicDataFilePath \brief The path to the file that contains this polymer chemistry definition's isotopic data. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_leftCap \brief The \l Formula that defines how of the left end of a polymer sequence of this \l PolChemDef needs to be capped in order to finalize the polymerization state of the \l Polymer sequence. \sa PolChemDef::m_rightCap */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_rightCap \brief The \l Formula that defines how of the right end of a polymer sequence of this \l PolChemDef needs to be capped in order to finalize the polymerization state of the \l Polymer sequence. \sa PolChemDef::m_leftCap */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_codeLength \brief The maximum length of a \l Monomer code in this defintion. The valid syntax of a Monomer code is that the first character of the code is uppercase and all the remaining ones are lowercase. The total number of characters cannot exceed m_codeLength. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_delimitedCodes \brief The set of \l Monomer codes separated by '@' characters, like "@Ala@Tyr@Phe@". */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_ionizer \brief The \l{Ionizer}{ionization agent} that governs the manner the \l Polymer sequences of this polymer chemistry definition are ionized by default. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::msp_isotopicData \brief The isotopic data defining the fundamentals of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_monomers \brief The container of \l{Monomer}s defined to be part of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_modifs \brief The container of \l{Modif}s defined to be part of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_crossLinkers The container of \l{CrossLinker}s defined to be part of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_cleavageAgents \brief The container of \l{CleavageAgent}s defining the various ways to cleave \l Polymer sequences of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_fragmentationPathways \brief The container of \l{FragmentationPathway}s defining the various ways to fragment \l Oligomer sequences of this \l PolChemDef instance. */ /*! \variable MsXpS::libXpertMassCore::PolChemDef::m_polChemDefs \brief The container of \l PolChemDef instances available in the repositories. */ /*! \variable MsXpS::libXpertMassCore::POL_CHEM_DEF_FILE_FORMAT_VERSION \brief The latest version of the format of the file containing the polymer chemistry definition. Brought to 1 30 january 2023 for massXpert2 Brought to 2 10 november 2024 for massXpert2 */ const int POL_CHEM_DEF_FILE_FORMAT_VERSION = 2; /*! \brief Constructs a polymer chemistry definition. */ PolChemDef::PolChemDef() { } /*! \brief Constructs a polymer chemistry definition on the basis of \a pol_chem_def_spec. The \a pol_chem_def_spec polymer chemistry definition specification provides the name of the polymer chemistry definition and of the file that contains it. */ PolChemDef::PolChemDef(const PolChemDefSpec &pol_chem_def_spec) { m_name = pol_chem_def_spec.getName(); m_xmlDataFilePath = pol_chem_def_spec.getFilePath(); } /*! \brief Destroys the polymer chemistry definition */ PolChemDef::~PolChemDef() { } /*! \brief Sets the \a name of this PolChemDef instance. */ void PolChemDef::setName(const QString &name) { m_name = name; } /*! \brief Returns the name of this PolChemDef instance. */ QString PolChemDef::getName() const { return m_name; } /*! \brief Sets the \a file_path of this PolChemDef instance. */ void PolChemDef::setXmlDataFilePath(const QString &file_path) { m_xmlDataFilePath = file_path; } /*! \brief Returns the file path of this PolChemDef instance. */ QString PolChemDef::getXmlDataFilePath() const { return m_xmlDataFilePath; } /*! \brief Returns the absolute directory path of this PolChemDef instance. */ QString PolChemDef::getXmlDataDirPath() const { // qDebug() << "The xml data file path:" << m_xmlDataFilePath; QFileInfo fileInfo(m_xmlDataFilePath); QDir dir(fileInfo.dir()); // qDebug() << "Returning the pol chem def data dir path:" << dir.absolutePath(); return dir.absolutePath(); } /*! \brief Sets the path of the isotopic data file to \a file_path. */ void PolChemDef::setIsotopicDataFilePath(const QString &file_path) { m_isotopicDataFilePath = file_path; } /*! \brief Returns the path of the isotopic data file. */ QString PolChemDef::getIsotopicDataFilePath() const { return m_isotopicDataFilePath; } /*! \brief Returns the path of the isotopic data file. The deduction is based on the fact that the file should have "isotopic-data.dat" as its name. */ QString PolChemDef::deduceIsotopicDataFilePath() const { // From the xml data file path, deduce the file name of the isotopic data. QFileInfo file_info(m_xmlDataFilePath); if(!file_info.exists()) qFatal( "Programming error. The polymer chemistry definition file could not be " "found."); QDir dir(file_info.dir()); QString isotopic_data_file_path = dir.absolutePath() + QDir::separator() + "isotopic-data.dat"; return isotopic_data_file_path; } /*! \brief Sets the left cap \a formula. */ void PolChemDef::setLeftCap(const Formula &formula) { m_leftCap = formula; } /*! \brief Returns the left cap formula. */ const Formula & PolChemDef::getLeftCap() const { return m_leftCap; } /*! \brief Sets the right cap \a formula. */ void PolChemDef::setRightCap(const Formula &formula) { m_rightCap = formula; } /*! \brief Returns the right cap formula. */ const Formula & PolChemDef::getRightCap() const { return m_rightCap; } /*! \brief Sets the \a code_length. */ void PolChemDef::setCodeLength(int code_length) { m_codeLength = code_length; } /*! \brief Returns the code length. */ int PolChemDef::getCodeLength() const { return m_codeLength; } /*! \brief Sets the ionizer to \a ionizer. */ void PolChemDef::setIonizer(const Ionizer &ionizer) { m_ionizer = ionizer; } /*! \brief Returns a const reference to the ionizer. */ const Ionizer & PolChemDef::getIonizerCstRef() const { return m_ionizer; } /*! \brief Returns a pointer to the ionizer. */ Ionizer & PolChemDef::getIonizerRef() { return m_ionizer; } /*! \brief Sets the isotopic data to \a isotopic_data_sp. */ void PolChemDef::setIsotopicDataSPtr(IsotopicDataSPtr isotopic_data_sp) { msp_isotopicData = isotopic_data_sp; } /*! \brief Returns the isotopic data as const shared pointer.. */ IsotopicDataCstSPtr PolChemDef::getIsotopicDataCstSPtr() const { return msp_isotopicData; } /*! \brief Returns the isotopic data as non-const shared pointer.. */ IsotopicDataSPtr PolChemDef::getIsotopicDataSPtr() { return msp_isotopicData; } //////////////////// MODIFS ////////////////////// //////////////////// MODIFS ////////////////////// /*! \brief Returns a const reference to the list of modifications. */ const std::vector & PolChemDef::getModifsCstRef() const { return m_modifs; } /*! \brief Returns a pointer to the list of modifications. */ std::vector & PolChemDef::getModifsRef() { return m_modifs; } /*! \brief Returns a pointer to the Modif instance known under \a name. If no Modif by that \a name is known, nullptr is returned. */ ModifCstSPtr PolChemDef::getModifCstSPtrByName(const QString &name) const { ModifsCstIterator the_iterator_cst = std::find_if( m_modifs.cbegin(), m_modifs.cend(), [name](const auto &modif_csp) { return modif_csp->getName() == name; }); if(the_iterator_cst == m_modifs.cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns the index of the \l Modif in the member container of these instances or -1 if that was not found. The search is performed using the \a name of the Modif. */ int PolChemDef::getModifIndexByName(const QString &name) const { ModifsCstIterator the_iterator_cst = std::find_if( m_modifs.cbegin(), m_modifs.cend(), [name](const auto &modif_csp) { bool same_name = (modif_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_modifs.cend()) return -1; return std::distance(m_modifs.cbegin(), the_iterator_cst); } //////////////////// MONOMERS ////////////////////// //////////////////// MONOMERS ////////////////////// /*! \brief Returns a const reference to the list of modifications. */ const std::vector & PolChemDef::getMonomersCstRef() const { return m_monomers; } /*! \brief Returns a pointer to the list of Monomer instances. */ std::vector & PolChemDef::getMonomersRef() { return m_monomers; } /*! \brief Returns a pointer to the Monomer instance known under \a name. If no Monomer by that \a name is known, nullptr is returned. */ MonomerSPtr PolChemDef::getMonomerCstSPtrByName(const QString &name) const { MonomersCstIterator the_iterator_cst = std::find_if( m_monomers.cbegin(), m_monomers.cend(), [name](const auto &monomer_csp) { return monomer_csp->getName() == name; }); if(the_iterator_cst == m_monomers.cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns a pointer to the Monomer instance known under \a code. If no Monomer by that \a code is known, nullptr is returned. */ MonomerSPtr PolChemDef::getMonomerCstSPtrByCode(const QString &code) const { MonomersCstIterator the_iterator_cst = std::find_if( m_monomers.cbegin(), m_monomers.cend(), [code](const auto &monomer_csp) { return monomer_csp->getCode() == code; }); if(the_iterator_cst == m_monomers.cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns the index of the \l Monomer in the member container of these instances or -1 if that was not found. The search is performed using the \a name of the Monomer. */ int PolChemDef::getMonomerIndexByName(const QString &name) const { MonomersCstIterator the_iterator_cst = std::find_if( m_monomers.cbegin(), m_monomers.cend(), [name](const auto &monomer_csp) { bool same_name = (monomer_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_monomers.cend()) return -1; return std::distance(m_monomers.cbegin(), the_iterator_cst); } /*! \brief Returns the index of the \l Monomer in the member container of these instances or -1 if that was not found. The search is performed using the \a code of the Monomer. */ int PolChemDef::getMonomerIndexByCode(const QString &code) const { MonomersCstIterator the_iterator_cst = std::find_if( m_monomers.cbegin(), m_monomers.cend(), [code](const auto &monomer_csp) { bool same_code = (monomer_csp->getCode() == code); return same_code; }); if(the_iterator_cst == m_monomers.cend()) return -1; return std::distance(m_monomers.cbegin(), the_iterator_cst); } //////////////////// CROSSLINKERS ////////////////////// //////////////////// CROSSLINKERS ////////////////////// /*! \brief Returns a const reference to the list of modifications. */ const std::vector & PolChemDef::getCrossLinkersCstRef() const { return m_crossLinkers; } /*! \brief Returns a pointer to the list of CrossLinker instances. */ std::vector & PolChemDef::getCrossLinkersRef() { return m_crossLinkers; } /*! \brief Returns a pointer to the CrossLinker instance known under \a name. If no CrossLinker by that \a name is known, nullptr is returned. */ CrossLinkerCstSPtr PolChemDef::getCrossLinkerCstSPtrByName(const QString &name) const { CrossLinkersCstIterator the_iterator_cst = std::find_if(m_crossLinkers.cbegin(), m_crossLinkers.cend(), [name](const auto &crossLinker_csp) { bool same_name = (crossLinker_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_crossLinkers.cend()) return nullptr; return (*the_iterator_cst); } //////////////////// CLEAVAGE AGENTS ////////////////////// //////////////////// CLEAVAGE AGENTS ////////////////////// /*! \brief Returns a const reference to the list of modifications. */ const std::vector & PolChemDef::getCleavageAgentsCstRef() const { return m_cleavageAgents; } /*! \brief Returns a pointer to the list of CleavageAgent instances. */ std::vector & PolChemDef::getCleavageAgentsRef() { return m_cleavageAgents; } /*! \brief Returns a pointer to the CleavageAgent instance known under \a name. If no CleavageAgent by that \a name is known, nullptr is returned. */ CleavageAgentCstSPtr PolChemDef::getCleavageAgentCstSPtrByName(const QString &name) const { CleavageAgentsCstIterator the_iterator_cst = std::find_if(m_cleavageAgents.cbegin(), m_cleavageAgents.cend(), [name](const auto &cleavage_agent_csp) { bool same_name = (cleavage_agent_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_cleavageAgents.cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns the index of the \l CleavageAgent in the member container of these instances or -1 if that was not found. The search is performed using the \a name of the CleavageAgent. */ int PolChemDef::getCleavageAgentIndexByName(const QString &name) const { CleavageAgentsCstIterator the_iterator_cst = std::find_if(m_cleavageAgents.cbegin(), m_cleavageAgents.cend(), [name](const auto &cleavage_agent_csp) { bool same_name = (cleavage_agent_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_cleavageAgents.cend()) return -1; return std::distance(m_cleavageAgents.cbegin(), the_iterator_cst); } //////////////////// FRAGMENTATION PATHWAYS ////////////////////// //////////////////// FRAGMENTATION PATHWAYS ////////////////////// /*! \brief Returns a const reference to the list of modifications. */ const std::vector & PolChemDef::getFragmentationPathwaysCstRef() const { return m_fragmentationPathways; } /*! \brief Returns a pointer to the list of FragmentationPathway instances. */ std::vector & PolChemDef::getFragmentationPathwaysRef() { return m_fragmentationPathways; } /*! \brief Returns a pointer to the FragmentationPathway instance known under \a name. If no FragmentationPathway by that \a name is known, nullptr is returned. */ FragmentationPathwayCstSPtr PolChemDef::getFragmentationPathwayCstSPtrByName(const QString &name) const { FragmentationPathwaysCstIterator the_iterator_cst = std::find_if(m_fragmentationPathways.cbegin(), m_fragmentationPathways.cend(), [name](const auto &fragmentation_pathway_csp) { bool same_name = (fragmentation_pathway_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_fragmentationPathways.cend()) return nullptr; return (*the_iterator_cst); } /*! \brief Returns the index of the \l FragmentationPathway in the member container of these instances or -1 if that was not found. The search is performed using the \a name of the FragmentationPathway. */ int PolChemDef::getFragmentationPathwayIndexByName(const QString &name) const { FragmentationPathwaysCstIterator the_iterator_cst = std::find_if(m_fragmentationPathways.cbegin(), m_fragmentationPathways.cend(), [name](const auto &cleavage_agent_csp) { bool same_name = (cleavage_agent_csp->getName() == name); return same_name; }); if(the_iterator_cst == m_fragmentationPathways.cend()) return -1; return std::distance(m_fragmentationPathways.cbegin(), the_iterator_cst); } /*! \brief Constructs a string with the codes of all the \l{Monomer}s in this PolChemDef instance. The codes are delimited by the '@' character. Returns true. */ bool PolChemDef::calculateDelimitedCodes() { // We have to produce a QString containing all the codes from the // monomers known to this polymer chemistry definition. m_delimitedCodes.clear(); m_delimitedCodes.append('@'); for(auto monomer_csp : m_monomers) { m_delimitedCodes.append(monomer_csp->getCode()); m_delimitedCodes.append('@'); } // Close the string with a delim char: m_delimitedCodes.append('@'); return true; } /*! \brief Returns a string with the codes of all the \l{Monomer}s in this PolChemDef instance. The codes are delimited by the '@' character. */ const QString & PolChemDef::getDelimitedCodes() { if(m_delimitedCodes.isEmpty()) calculateDelimitedCodes(); return m_delimitedCodes; } /*! \brief Returns a string list with the mass difference between all the \l{Monomer}s in this PolChemDef instace. If the difference is below the \a threshold, the difference is added to the string list, otherwise it is skipped. The masses that are compared between every two monomers is of \a mass_type. */ QStringList PolChemDef::differenceBetweenMonomers(double threshold, Enums::MassType mass_type) { // qDebug() // << "threshold" << threshold; QStringList difference_list; if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG) { qDebug() << "Please set the mass type " "to either Enums::MassType::MONO or " "Enums::MassType::AVG"; return difference_list; } // We iterate in the list of monomers and compute a difference of // mass for each monomer with respect to all the other ones. If // the mass difference is less or equal to the threshold, then the // pair is returned. if(m_monomers.size() < 2) return difference_list; MonomersCstIterator iterator_outer_loop_cst = m_monomers.cbegin(); while(iterator_outer_loop_cst != m_monomers.cend()) { Monomer outer_monomer = *(*iterator_outer_loop_cst); outer_monomer.calculateMasses(/*IsotopicDataCstSPtr*/ nullptr); MonomersCstIterator iterator_inner_loop_cst = m_monomers.cbegin(); while(iterator_inner_loop_cst != m_monomers.cend()) { if(*iterator_outer_loop_cst == *iterator_inner_loop_cst) { ++iterator_inner_loop_cst; continue; } Monomer inner_monomer = *(*iterator_inner_loop_cst); inner_monomer.calculateMasses(/*IsotopicDataCstSPtr*/ nullptr); // At this point we have the masses for both monomers. double mass_diff = 0; if(mass_type == Enums::MassType::MONO) { mass_diff = outer_monomer.getMass(Enums::MassType::MONO) - inner_monomer.getMass(Enums::MassType::MONO); } else //(monoOrAvg == Enums::MassType::AVG) { mass_diff = outer_monomer.getMass(Enums::MassType::AVG) - inner_monomer.getMass(Enums::MassType::AVG); } // At this point, make sure that diff is within the // threshold. Note that to avoid duplicates, we remove all // values that are negative. if(mass_diff >= 0 && mass_diff <= threshold) { QString line = QString("%1 - %2 = %3") .arg(outer_monomer.getName()) .arg(inner_monomer.getName()) .arg(mass_diff); difference_list.append(line); } ++iterator_inner_loop_cst; } ++iterator_outer_loop_cst; } return difference_list; } //////////////// OPERATORS ///////////////////// /*! \brief Returns true if this PolChemDef is equal to \a other, false otherwise. The comparison is deep, with all the various chemical entities in the various containers being compared (not their pointers, but the objects themselves). \note The names and the data file paths are not compared, because this comparison operator is aimed at comparing the chemistries of the PolChemDef. */ bool PolChemDef::isChemicallyEquivalent(const PolChemDef &other) const { if(&other == this) return true; if(m_name != other.m_name) { qDebug() << "The names are not identical."; } if(m_leftCap != other.m_leftCap) { qDebug() << "The left caps are not identical."; return false; } if(m_rightCap != other.m_rightCap) { qDebug() << "The right caps are not identical."; return false; } if(m_codeLength != other.m_codeLength) { qDebug() << "The code lengths are not identical."; return false; } if(m_delimitedCodes != other.m_delimitedCodes) { qDebug() << "The delimited codes are not identical."; return false; } if(m_ionizer != other.m_ionizer) { qDebug() << "The ionizers are not identical."; return false; } if(msp_isotopicData != nullptr && other.msp_isotopicData != nullptr) { qDebug() << "Now checking the isotopic data"; if(*msp_isotopicData.get() != *other.msp_isotopicData.get()) { qDebug() << "The isotopic data are not identical."; return false; } } if(m_modifs.size() != other.m_modifs.size()) { qDebug() << "The modifs containers have not the same sizes."; return false; } ModifsCstIterator this_modif_iterator_cst = m_modifs.cbegin(); ModifsCstIterator other_modif_iterator_cst = other.m_modifs.cbegin(); while(this_modif_iterator_cst != m_modifs.cend() && other_modif_iterator_cst != other.m_modifs.cend()) { const Modif this_modif = *(*this_modif_iterator_cst); const Modif other_modif = *(*other_modif_iterator_cst); if(this_modif != other_modif) { qDebug() << "At least one modification is not identical in both " "PolChemDef entities."; return false; } ++this_modif_iterator_cst; ++other_modif_iterator_cst; } qDebug() << "The Modif instances are identical."; if(m_monomers.size() != other.m_monomers.size()) { qDebug() << "The monomers containers have not the same sizes."; return false; } MonomersCstIterator this_monomer_iterator_cst = m_monomers.cbegin(); MonomersCstIterator other_monomer_iterator_cst = other.m_monomers.cbegin(); while(this_monomer_iterator_cst != m_monomers.cend() && other_monomer_iterator_cst != other.m_monomers.cend()) { const Monomer this_monomer = *(*this_monomer_iterator_cst); const Monomer other_monomer = *(*other_monomer_iterator_cst); if(this_monomer != other_monomer) { qDebug() << "At least one monomer is not identical in both PolChemDef " "entities."; return false; } ++this_monomer_iterator_cst; ++other_monomer_iterator_cst; } qDebug() << "The Monomer instances are identical."; if(m_crossLinkers.size() != other.m_crossLinkers.size()) { qDebug() << "The cross-linkers containers have not the same sizes."; return false; } CrossLinkersCstIterator this_cross_linker_iterator_cst = m_crossLinkers.cbegin(); CrossLinkersCstIterator other_cross_linker_iterator_cst = other.m_crossLinkers.cbegin(); while(this_cross_linker_iterator_cst != m_crossLinkers.cend() && other_cross_linker_iterator_cst != other.m_crossLinkers.cend()) { const CrossLinker this_cross_linker = *(*this_cross_linker_iterator_cst); const CrossLinker other_cross_linker = *(*other_cross_linker_iterator_cst); if(this_cross_linker != other_cross_linker) { qDebug().noquote() << "At least one cross-linker is not identical in both " "PolChemDef entities: \n" << this_cross_linker.toString() << "\nvs\n" << other_cross_linker.toString(); return false; } ++this_cross_linker_iterator_cst; ++other_cross_linker_iterator_cst; } qDebug() << "The CrossLinker instances are identical."; if(m_cleavageAgents.size() != other.m_cleavageAgents.size()) { qDebug() << "The cleavage specifications containers have not the same sizes."; return false; } CleavageAgentsCstIterator this_cleavage_agent_iterator_cst = m_cleavageAgents.cbegin(); CleavageAgentsCstIterator other_cleavage_agent_iterator_cst = other.m_cleavageAgents.cbegin(); while(this_cleavage_agent_iterator_cst != m_cleavageAgents.cend() && other_cleavage_agent_iterator_cst != other.m_cleavageAgents.cend()) { CleavageAgent this_cleavage_agent; this_cleavage_agent.initialize(*(*this_cleavage_agent_iterator_cst)); CleavageAgent other_cleavage_agent; other_cleavage_agent.initialize(*(*other_cleavage_agent_iterator_cst)); if(this_cleavage_agent != other_cleavage_agent) { qDebug().noquote() << "At least one cleavage specification is not identical in " "both PolChemDef entities:\n" << this_cleavage_agent.toString() << "\nvs\n" << other_cleavage_agent.toString(); return false; } ++this_cleavage_agent_iterator_cst; ++other_cleavage_agent_iterator_cst; } qDebug() << "The CleavageAgent instances are identical."; if(m_fragmentationPathways.size() != other.m_fragmentationPathways.size()) { qDebug() << "The fragmentation specifications containers have not the " "same sizes."; return false; } FragmentationPathwaysCstIterator this_fragmentation_pathway_iterator_cst = m_fragmentationPathways.cbegin(); FragmentationPathwaysCstIterator other_fragmentation_pathway_iterator_cst = other.m_fragmentationPathways.cbegin(); while(this_fragmentation_pathway_iterator_cst != m_fragmentationPathways.cend() && other_fragmentation_pathway_iterator_cst != other.m_fragmentationPathways.cend()) { const FragmentationPathway this_fragmentation_pathway = *(*this_fragmentation_pathway_iterator_cst); const FragmentationPathway other_fragmentation_pathway = *(*other_fragmentation_pathway_iterator_cst); if(this_fragmentation_pathway != other_fragmentation_pathway) { qDebug() << "At least one fragmentation specification is not " "identical in both PolChemDef entities."; return false; } ++this_fragmentation_pathway_iterator_cst; ++other_fragmentation_pathway_iterator_cst; } qDebug() << "The FragmentationPathway instances are identical."; return true; } /*! \brief Returns true if this PolChemDef is equal to \a other, false otherwise. The comparison is deep, with all the various chemical entities in the various containers being compared (not their pointers, but the objects themselves). \note The names and the data file paths are not compared, because this comparison operator is aimed at comparing the chemistries of the PolChemDef. */ bool PolChemDef::operator==(const PolChemDef &other) const { if(&other == this) return true; if(!isChemicallyEquivalent(other)) { qDebug() << "The PolChemDef are not chemically identical."; return false; } if(m_name != other.m_name) { qDebug() << "The names are not identical."; } if(m_xmlDataFilePath != other.m_xmlDataFilePath) { qDebug() << "The definition file paths are not identical."; } if(m_isotopicDataFilePath != other.m_isotopicDataFilePath) { qDebug() << "The isotopic data file paths are not identical."; } return true; } /*! \brief Returns true if \c this and \a other differ. Returns the negated result of operator==(). */ bool PolChemDef::operator!=(const PolChemDef &other) const { if(&other == this) return false; return !operator==(other); } /*! \brief Loads the isotopic data stored in the file \a file_path. The data loaded from file are parsed and validated. Upon parsing of the file, the data are stored in the \a pol_chem_def_sp instance's isotopic data member. If \a file_path is empty, it is reconstructed using the directory of the polymer chemistry definition and the fact that the isotopic data file name is "isotopic-data.dat". Returns the count of parsed isotopes. */ std::size_t PolChemDef::loadIsotopicData(PolChemDefSPtr pol_chem_def_sp, const QString &file_path) { // There are three situations: // // 1. The file_path exists and the data are loaded from there. // // 2. A file named isotopic-data.dat is found in the polymer chemistry // definition directory and it is loaded. // // 3. No file named isotopic-data.dat is found in the polymer chemistry // defintion directory and then data are loaded from the IsoSpec tables. // If the provided argument is not empty and actually describes an // existing file, then save the data there. QString local_file_path(file_path); QFileInfo local_file_info(local_file_path); if(local_file_path.isEmpty() || !local_file_info.exists()) { // qDebug() << "The provided file name" << file_path << "does not // exist."; // Then try the member datum that provides the name of the file from // which to load the isotopic data. local_file_path = pol_chem_def_sp->getIsotopicDataFilePath(); local_file_info.setFile(local_file_path); if(local_file_path.isEmpty() || !local_file_info.exists()) { // qDebug() << "The member datum file name " << local_file_path //<< "does not exist."; // Last resort: deduce the isotopic data file name from the // directory of the polymer chemistry definition. local_file_path = pol_chem_def_sp->deduceIsotopicDataFilePath(); // qDebug() << "Crafted the isotopic-data.dat file path:" //<< local_file_path; // Finally for a later last check: local_file_info.setFile(local_file_path); } } // qDebug() << "Allocating brand new isotopic data instance."; // At this point we can delete the pre-exsting isotopic data. pol_chem_def_sp->msp_isotopicData = std::make_shared(); // Allocate a specific handler to the kind of isotopic data (library // tables or user file). std::size_t count = 0; if(local_file_path.isEmpty() || !local_file_info.exists()) { qDebug() << "Loading the isotopic data from the library."; IsotopicDataLibraryHandler isotopic_data_handler( pol_chem_def_sp->msp_isotopicData); qsizetype non_isotope_skipped_items = 0; count = isotopic_data_handler.loadData(non_isotope_skipped_items); } else { qDebug() << "Loading the isotopic data from file:" << local_file_path; IsotopicDataUserConfigHandler isotopic_data_handler( pol_chem_def_sp->msp_isotopicData); count = isotopic_data_handler.loadData(local_file_path); if(!count) { qDebug() << "Failed to load any isotopic data."; return false; } // Now set the file path to the pol chem def. pol_chem_def_sp->setIsotopicDataFilePath(local_file_path); } if(!count) { qDebug() << "Failed to load any isotopic data."; return false; } return count; } /*! \brief Writes the isotopic data in the \a pol_chem_def_sp PolChemDef instance to file \a file_path. Returns the count of isotopes written to file. */ std::size_t PolChemDef::writeIsotopicData(PolChemDefSPtr pol_chem_def_sp, const QString &file_path) { // There are three situations: // // 1. The file_path is not empty and the data are saved there. // // 2. The file_path is empty and the data are stored in the member file // path name (if not empty). // // 3. The file path is crafted from the directory of the polymer // chemistry definition. // Whatever the situation, the isotopic data handler we need here is // this one: IsotopicDataUserConfigHandler isotopic_data_handler( pol_chem_def_sp->msp_isotopicData); // We'll instantiate the proper isotopic data handler depending on the // situation. if(!file_path.isEmpty()) { // We have a file name, store in there. pol_chem_def_sp->setIsotopicDataFilePath(file_path); return isotopic_data_handler.writeData(file_path); } // Check the member datum. if(!pol_chem_def_sp->m_isotopicDataFilePath.isEmpty()) { // We have a file name, store in there. return isotopic_data_handler.writeData( pol_chem_def_sp->m_isotopicDataFilePath); } // Last resort: deduce the isotopic data file name from the directory // of the polymer chemistry definition. QString local_file_path = pol_chem_def_sp->getXmlDataDirPath(); QFileInfo local_file_info(local_file_path); if(!local_file_info.exists()) qFatal( "Programming error. At this stage the name of the polymer " "chemistry definition file should be known."); QDir dir(local_file_info.dir()); local_file_path = dir.absolutePath() + QDir::separator() + "isotopic-data.dat"; pol_chem_def_sp->setIsotopicDataFilePath(local_file_path); return isotopic_data_handler.writeData(local_file_path); } /*! \brief Parses the polymer chemistry definition file and updates \a pol_chem_def_sp accordingly. Upon parsing of the file and validation of the data, the \a pol_chem_def_sp is updated, essentially initializing it with the data from the file. Note that the \a pol_chem_def_sp should have a working set of isotopic data. Returns true if the parsing was successful and false otherwise. */ bool PolChemDef::renderXmlPolChemDefFileVersion1(PolChemDefSPtr pol_chem_def_sp) { qDebug() << "Rendering PolChemDef file VERSION 1."; if(pol_chem_def_sp == nullptr && pol_chem_def_sp.get() == nullptr) qFatalStream() << "Programming error. The PolChemDef pointer cannot be nullptr."; // qDebug() << "The PolChemDef *:" << pol_chem_def_sp.get() //<< "and usage:" << pol_chem_def_sp.use_count(); ///////////////////// ATTENTION /////////////////// // Before reading the polymer chemistry data file, we need to make sure // we actually have read the isotopic data! // qDebug() << "First check if we have isotopic data ready."; if(pol_chem_def_sp->getIsotopicDataSPtr() == nullptr) { // qDebug() << "No isotopic data found in the polymer chemistry " // "definition, need to load the data."; // First read the data! std::size_t count = pol_chem_def_sp->loadIsotopicData(pol_chem_def_sp); if(!count) qFatal("Programming error. The isotopic data could not be loaded."); // qDebug() << "At this point the isotopic data were loaded fine // with" //<< count << "isotopes loaded."; } QDomDocument doc("polChemDefData"); QDomElement element; QDomElement child; QDomElement indentedChild; QFile file(pol_chem_def_sp->m_xmlDataFilePath); // qDebug() << "The polymer chemistry definition file:" // << pol_chem_def_sp->m_xmlDataFilePath; // The general structure of the file we are reading is this: // // // // protein // +H // +OH // 1 // // +H // 1 // 1 // // // // Glycine // G // C2H3NO // // // // // Phosphorylation // -H+H2PO3 // // // // // CyanogenBromide // M/ // // M // -CH2S+O // // // // // // a // LE // -C1O1 // // a-fgr-1 // +H200 // E // D // F // comment here! // // // // // if(!file.open(QIODevice::ReadOnly)) return false; if(!doc.setContent(&file)) { qDebug() << "Failed to set doc content."; file.close(); return false; } file.close(); // qDebug() << "Closed the polymer chemistry definition file."; element = doc.documentElement(); if(element.tagName() != "polchemdefinition") { qDebug() << "Polymer chemistry definition file is erroneous\n"; return false; } /////////////////////////////////////////////// // Check the version of the document. QString text; if(!element.hasAttribute("version")) text = "1"; else text = element.attribute("version"); qDebug() << "The format of the definition:" << text; bool ok = false; int version = text.toInt(&ok, 10); if(version != 1 || !ok) { qDebug() << "Polymer chemistry definition file has bad " "version number:" << version; return false; } ////////////////////////////////////////////// // child = element.firstChildElement(); if(child.tagName() != "polchemdefdata") { qDebug() << "Polymer chemistry definition file is erroneous\n"; return false; } // child = child.firstChildElement(); if(child.tagName() != "name") return false; pol_chem_def_sp->m_name = child.text(); // child = child.nextSiblingElement(); if(child.tagName() != "leftcap") return false; pol_chem_def_sp->m_leftCap.setActionFormula(child.text()); // child = child.nextSiblingElement(); if(child.tagName() != "rightcap") return false; pol_chem_def_sp->m_rightCap.setActionFormula(child.text()); // child = child.nextSiblingElement(); if(child.tagName() != "codelen") return false; ok = false; pol_chem_def_sp->m_codeLength = child.text().toInt(&ok); if(pol_chem_def_sp->m_codeLength == 0 && !ok) return false; qDebug() << "Now set the code length to" << pol_chem_def_sp->m_codeLength; // child = child.nextSiblingElement(); if(child.tagName() != "ionizerule") return false; // It is essential that we endow the Ionizer with the IsotopicData ! pol_chem_def_sp->m_ionizer.setIsotopicDataCstSPtr( pol_chem_def_sp->getIsotopicDataCstSPtr()); // Now we can render the Ionizer by looking into the XML // element. if(!pol_chem_def_sp->m_ionizer.renderXmlIonizeRuleElement(child)) return false; // Finally, we have to ascertain that the Ionizer is valid. ErrorList error_list; if(!pol_chem_def_sp->m_ionizer.validate(&error_list)) { qCritical() << "The Ionizer is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); return false; } qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "monomers") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { qDebug() << "Now rendering one of all the mnm elements."; if(indentedChild.tagName() != "mnm") { qCritical() << "The expected element was not found."; return false; } // Constructing a Monomer using the XML element ensures // that the masses are properly calculated for it, so // that MonomerCstSPtr point to fully qualified Monomer // instances that properly play the role of reference // throughout the chemical entities that involve Monomer // instances in the context of this PolChemDef. MonomerSPtr monomer_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!monomer_sp->isValid()) { qDebug() << "Failed to render mnm element."; monomer_sp.reset(); return false; } pol_chem_def_sp->m_monomers.push_back(monomer_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of MonomerList:" << pol_chem_def_sp->getMonomersCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "modifs") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "mdf") return false; ModifSPtr modif_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!modif_sp->isValid()) { qCritical() << "Failed to render the Modif instance from an XML element."; modif_sp.reset(); return false; } pol_chem_def_sp->m_modifs.push_back(modif_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of ModifList:" << pol_chem_def_sp->getModifsCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // // Note that crosslinkers have appeared since version 3. child = child.nextSiblingElement(); if(child.tagName() != "crosslinkers") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "clk") return false; CrossLinkerSPtr crossLinker_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!crossLinker_sp->isValid()) { qCritical() << "Failed to render the CrossLinker instance from an " "XML element."; crossLinker_sp.reset(); return false; } pol_chem_def_sp->m_crossLinkers.push_back(crossLinker_sp); qDebug() << "Rendered CrossLinker: " << crossLinker_sp->getName() << "with formula:" << crossLinker_sp->getFormula() << "with masses:" << crossLinker_sp->getMass(Enums::MassType::MONO) << "/" << crossLinker_sp->getMass(Enums::MassType::AVG); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of CrossLinker container:" << pol_chem_def_sp->getCrossLinkersCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "cleavespecs") { qCritical() << "Failed to load PolChemDef file: element " "not found."; return false; } indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "cls") { qCritical() << "Failed to load PolChemDef file: element not found."; return false; } CleavageAgentSPtr cleavage_agent_sp = std::make_shared( pol_chem_def_sp, indentedChild, version); if(!cleavage_agent_sp->isValid()) { qCritical() << "Failed to render the CleaveSpec instance from an " "XML element."; cleavage_agent_sp.reset(); return false; } qDebug() << "Rendered CleavageAgent: " << cleavage_agent_sp->getName() << "with pattern:" << cleavage_agent_sp->getPattern(); pol_chem_def_sp->m_cleavageAgents.push_back(cleavage_agent_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of CleavageAgent container:" << pol_chem_def_sp->getCleavageAgentsCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "fragspecs") { qCritical() << "Failed to load PolChemDef file: element not found."; return false; } indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "fgs") { qCritical() << "Failed to load PolChemDef file: element not found."; return false; } FragmentationPathwaySPtr fragmentation_pathway_sp = std::make_shared( pol_chem_def_sp, indentedChild, version); if(!fragmentation_pathway_sp->isValid()) { qCritical() << "Failed to render the FragSpec instance from an " "XML element."; fragmentation_pathway_sp.reset(); return false; } qDebug() << "Rendered FragmentationPathway: " << fragmentation_pathway_sp->getName(); pol_chem_def_sp->m_fragmentationPathways.push_back( fragmentation_pathway_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of FragmentationPathway container:" << pol_chem_def_sp->getFragmentationPathwaysCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Done rendering the PolChemDef file. Setting m_isValid to true " "and returning it."; pol_chem_def_sp->m_isValid = true; return pol_chem_def_sp->m_isValid; } /*! \brief Parses the polymer chemistry definition file and updates \a pol_chem_def_sp accordingly. Upon parsing of the file and validation of the data, the \a pol_chem_def_sp is updated, essentially initializing it with the data from the file. Note that the \a pol_chem_def_sp should have a working set of isotopic data. Returns true if the parsing was successful and false otherwise. */ bool PolChemDef::renderXmlPolChemDefFileVersion2(PolChemDefSPtr pol_chem_def_sp) { qDebug() << "Rendering PolChemDef file VERSION 2."; if(pol_chem_def_sp == nullptr && pol_chem_def_sp.get() == nullptr) qFatalStream() << "Programming error. The PolChemDef pointer cannot be nullptr."; // qDebug() << "The PolChemDef *:" << pol_chem_def_sp.get() //<< "and usage:" << pol_chem_def_sp.use_count(); ///////////////////// ATTENTION /////////////////// // Before reading the polymer chemistry data file, we need to make sure // we actually have read the isotopic data! // qDebug() << "First check if we have isotopic data ready."; if(pol_chem_def_sp->getIsotopicDataSPtr() == nullptr) { // qDebug() << "No isotopic data found in the polymer chemistry " // "definition, need to load the data."; // First read the data! std::size_t count = pol_chem_def_sp->loadIsotopicData(pol_chem_def_sp); if(!count) qFatal("Programming error. The isotopic data could not be loaded."); // qDebug() << "At this point the isotopic data were loaded fine // with" //<< count << "isotopes loaded."; } QDomDocument doc("polChemDefData"); QDomElement element; QDomElement child; QDomElement indentedChild; QFile file(pol_chem_def_sp->m_xmlDataFilePath); // qDebug() << "The polymer chemistry definition file:" // << pol_chem_def_sp->m_xmlDataFilePath; // The general structure of the file we are reading is this: // // // // protein // +H // +OH // 1 // // +H // 1 // 1 // // // // Glycine // G // C2H3NO // // // // // Phosphorylation // -H+H2PO3 // // // // // CyanogenBromide // M/ // // M // -CH2S+O // // // // // // a // LE // -C1O1 // // a-fgr-1 // +H200 // E // D // F // comment here! // // // // // if(!file.open(QIODevice::ReadOnly)) return false; if(!doc.setContent(&file)) { qDebug() << "Failed to set doc content."; file.close(); return false; } file.close(); // qDebug() << "Closed the polymer chemistry definition file."; element = doc.documentElement(); if(element.tagName() != "polchemdefinition") { qDebug() << "Polymer chemistry definition file is erroneous\n"; return false; } /////////////////////////////////////////////// // Check the version of the document. QString text; if(!element.hasAttribute("version")) text = "1"; else text = element.attribute("version"); qDebug() << "The format of the definition:" << text; bool ok = false; int version = text.toInt(&ok, 10); if(version != 2 || !ok) { qDebug() << "Polymer chemistry definition file has bad " "version number:" << version; return false; } ////////////////////////////////////////////// // child = element.firstChildElement(); if(child.tagName() != "polchemdefdata") { qDebug() << "Polymer chemistry definition file is erroneous\n"; return false; } // child = child.firstChildElement(); if(child.tagName() != "name") return false; pol_chem_def_sp->m_name = child.text(); // child = child.nextSiblingElement(); if(child.tagName() != "leftcap") return false; pol_chem_def_sp->m_leftCap.setActionFormula(child.text()); // child = child.nextSiblingElement(); if(child.tagName() != "rightcap") return false; pol_chem_def_sp->m_rightCap.setActionFormula(child.text()); // child = child.nextSiblingElement(); if(child.tagName() != "codelen") return false; ok = false; pol_chem_def_sp->m_codeLength = child.text().toInt(&ok); if(pol_chem_def_sp->m_codeLength == 0 && !ok) return false; qDebug() << "Set the code length to" << pol_chem_def_sp->m_codeLength; // child = child.nextSiblingElement(); if(child.tagName() != "ionizerule") return false; // It is essential that we endow the Ionizer with the IsotopicData ! pol_chem_def_sp->m_ionizer.setIsotopicDataCstSPtr( pol_chem_def_sp->getIsotopicDataCstSPtr()); // Now we can render the Ionizer by looking into the XML // element. if(!pol_chem_def_sp->m_ionizer.renderXmlIonizeRuleElement(child)) return false; // Finally, we have to ascertain that the Ionizer is valid. ErrorList error_list; if(!pol_chem_def_sp->m_ionizer.validate(&error_list)) { qCritical() << "The Ionizer is invalid, with errors:" << Utils::joinErrorList(error_list, ", "); return false; } qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "monomers") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { qDebug() << "Now rendering one of all the mnm elements."; if(indentedChild.tagName() != "mnm") { qCritical() << "The expected element was not found."; return false; } // Constructing a Monomer using the XML element ensures // that the masses are properly calculated for it, so // that MonomerCstSPtr point to fully qualified Monomer // instances that properly play the role of reference // throughout the chemical entities that involve Monomer // instances in the context of this PolChemDef. MonomerSPtr monomer_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!monomer_sp->isValid()) { qDebug() << "Failed to render mnm element."; monomer_sp.reset(); return false; } pol_chem_def_sp->m_monomers.push_back(monomer_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of MonomerList:" << pol_chem_def_sp->getMonomersCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "modifs") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "mdf") return false; ModifSPtr modif_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!modif_sp->isValid()) { qCritical() << "Failed to render the Modif instance from an XML element."; modif_sp.reset(); return false; } pol_chem_def_sp->m_modifs.push_back(modif_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of ModifList:" << pol_chem_def_sp->getModifsCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // // Note that crosslinkers have appeared since version 3. child = child.nextSiblingElement(); if(child.tagName() != "crosslinkers") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "clk") return false; CrossLinkerSPtr crossLinker_sp = std::make_shared(pol_chem_def_sp, indentedChild, version); if(!crossLinker_sp->isValid()) { qCritical() << "Failed to render the CrossLinker instance from an " "XML element."; crossLinker_sp.reset(); return false; } pol_chem_def_sp->m_crossLinkers.push_back(crossLinker_sp); // qDebug() << "Rendered CrossLinker: " << crossLinker_csp->getName() // << "with formula:" << crossLinker_csp->getFormula() // << "with masses:" << // crossLinker_csp->getMass(Enums::MassType::MONO) // << "/" << crossLinker_csp->getMass(Enums::MassType::AVG); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of CrossLinker container:" << pol_chem_def_sp->getCrossLinkersCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "cleavageagents") { qCritical() << "Failed to load PolChemDef file: element " "not found."; return false; } indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "cla") { qCritical() << "Failed to load PolChemDef file: element not found."; return false; } qDebug() << "Will allocate a new CleavageAgent instance->shared pointer."; CleavageAgentSPtr cleavage_agent_sp = std::make_shared( pol_chem_def_sp, indentedChild, version); if(!cleavage_agent_sp->isValid()) { qCritical() << "Failed to render the CleavageAgent instance from an " "XML element."; cleavage_agent_sp.reset(); return false; } pol_chem_def_sp->m_cleavageAgents.push_back(cleavage_agent_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of CleavageAgent container:" << pol_chem_def_sp->getCleavageAgentsCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Now starting the rendering of the element."; // child = child.nextSiblingElement(); if(child.tagName() != "fragmentationpathways") return false; indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() != "fgp") { qCritical() << "Failed to load PolChemDef file: element not found."; return false; } FragmentationPathwaySPtr fragmentation_pathway_sp = std::make_shared( pol_chem_def_sp, indentedChild, version); if(!fragmentation_pathway_sp->isValid()) { qCritical() << "Failed to render the FragmentationPathway instance from an " "XML element."; fragmentation_pathway_sp.reset(); return false; } pol_chem_def_sp->m_fragmentationPathways.push_back( fragmentation_pathway_sp); indentedChild = indentedChild.nextSiblingElement(); } qDebug() << "Size of FragmentationPathway container:" << pol_chem_def_sp->getFragmentationPathwaysCstRef().size() << "pol chem def usage:" << pol_chem_def_sp.use_count(); qDebug() << "Done rendering the PolChemDef file. Setting m_isValid to true " "and returning it."; pol_chem_def_sp->m_isValid = true; return pol_chem_def_sp->m_isValid; } /*! \brief Parses the polymer chemistry definition file and updates \a pol_chem_def_sp accordingly. Upon parsing of the file and validation of the data, the \a pol_chem_def_sp is updated, essentially initializing it with the data from the file. Note that the \a pol_chem_def_sp should have a working set of isotopic data. Returns true if the parsing was successful and false otherwise. */ bool PolChemDef::renderXmlPolChemDefFile(PolChemDefSPtr pol_chem_def_sp) { if(pol_chem_def_sp == nullptr && pol_chem_def_sp.get() == nullptr) qFatalStream() << "Programming error. The PolChemDef pointer cannot be nullptr."; // qDebug() << "The PolChemDef *:" << pol_chem_def_sp.get() //<< "and usage:" << pol_chem_def_sp.use_count(); ///////////////////// ATTENTION /////////////////// // Before reading the polymer chemistry data file, we need to make sure // we actually have read the isotopic data! // qDebug() << "First check if we have isotopic data ready."; if(pol_chem_def_sp->getIsotopicDataSPtr() == nullptr) { // qDebug() << "No isotopic data found in the polymer chemistry " // "definition, need to load the data."; // First read the data! std::size_t count = pol_chem_def_sp->loadIsotopicData(pol_chem_def_sp); if(!count) qFatal("Programming error. The isotopic data could not be loaded."); // qDebug() << "At this point the isotopic data were loaded fine // with" //<< count << "isotopes loaded."; } QDomDocument doc("polChemDefData"); QDomElement element; QDomElement child; QDomElement indentedChild; QFile file(pol_chem_def_sp->m_xmlDataFilePath); // qDebug() << "The polymer chemistry definition file:" // << pol_chem_def_sp->m_xmlDataFilePath; // The general structure of the file we are reading is this: // // // // protein // +H // +OH // 1 // // +H // 1 // 1 // // // // Glycine // G // C2H3NO // // // // // Phosphorylation // -H+H2PO3 // // // // // CyanogenBromide // M/ // // M // -CH2S+O // // // // // // a // LE // -C1O1 // // a-fgr-1 // +H200 // E // D // F // comment here! // // // // // if(!file.open(QIODevice::ReadOnly)) return false; if(!doc.setContent(&file)) { qDebug() << "Failed to set doc content."; file.close(); return false; } file.close(); // qDebug() << "Closed the polymer chemistry definition file."; element = doc.documentElement(); if(element.tagName() != "polchemdefinition") { qDebug() << "Polymer chemistry definition file is erroneous\n"; return false; } /////////////////////////////////////////////// // Check the version of the document. QString text; if(!element.hasAttribute("version")) text = "1"; else text = element.attribute("version"); qDebug() << "The format of the definition:" << text; bool ok = false; int version = text.toInt(&ok, 10); if(version < 1 || !ok) { qDebug() << "Polymer chemistry definition file has bad " "version number:" << version; return false; } file.close(); qDebug() << "The version of the Polymer Chemistry Definition file is:" << version; if(version == 1) return PolChemDef::renderXmlPolChemDefFileVersion1(pol_chem_def_sp); else if(version == 2) return PolChemDef::renderXmlPolChemDefFileVersion2(pol_chem_def_sp); return false; } /*! \brief Returns a string with the XML DTD for a polymer chemistry definition file. */ QString PolChemDef::formatXmlDtd() { QString string = QString( "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "]>\n"); return string; } /*! \brief Writes the polymer chemistry definition to file. The file's name is from m_xmlDataFilePath. Returns true if successful, false otherwise. */ bool PolChemDef::writeXmlFile() { QString indent(" "); QString lead; int offset = 0; int iter = 0; // We are asked to send an xml description of the polymer chemistry // definition. QFile file(m_xmlDataFilePath); if(!file.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open file" << m_xmlDataFilePath << "for writing."; return false; } QTextStream stream(&file); stream.setEncoding(QStringConverter::Utf8); // The DTD stream << formatXmlDtd(); // Open the element. stream << QString("\n") .arg(POL_CHEM_DEF_FILE_FORMAT_VERSION); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } // Open the element. stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1%2\n").arg(lead).arg(m_name); stream << QString("%1%2\n") .arg(lead) .arg(m_leftCap.getActionFormula(/*with_title*/ true)); stream << QString("%1%2\n") .arg(lead) .arg(m_rightCap.getActionFormula(/*with_title*/ true)); stream << QString("%1%2\n").arg(lead).arg(m_codeLength); // Before writing the ionization rule, set the level to 1. This // member datum is set to 0 in the constructor. m_ionizer.setLevel(1); stream << m_ionizer.formatXmlIonizeRuleElement(offset); stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } for(const auto &monomer_csp : m_monomers) stream << monomer_csp->formatXmlMnmElement(offset); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } for(const auto &modif_csp : m_modifs) stream << modif_csp->formatXmlMdfElement(offset); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } for(const auto &cross_linker_csp : m_crossLinkers) stream << cross_linker_csp->formatXmlClkElement(offset); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } for(const auto &cleavage_agent_csp : m_cleavageAgents) stream << cleavage_agent_csp->formatXmlClaElement(offset); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); stream << QString("%1\n").arg(lead); // Prepare the lead. ++offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } for(const auto &fragmentation_pathway_csp : m_fragmentationPathways) stream << fragmentation_pathway_csp->formatXmlFgpElement(offset); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); // Prepare the lead. --offset; lead.clear(); iter = 0; while(iter < offset) { lead += indent; ++iter; } stream << QString("%1\n").arg(lead); file.close(); return true; } /*! \brief Validates this PolChemDef instance by checking the validity of all its members data. Returns true if validation was successful, false otherwise. Any error is added to \a error_list_p if non-nullptr. */ bool PolChemDef::validate(ErrorList *error_list_p) const { // So we can return immediately upon error. m_isValid = false; if(m_isotopicDataFilePath.isEmpty()) { if(error_list_p != nullptr) error_list_p->push_back("The IsotopicData file path is empty."); return false; } QFileInfo file_info(m_isotopicDataFilePath); if(!file_info.exists()) { if(error_list_p != nullptr) error_list_p->push_back("The IsotopicData file cannot be found."); return false; } if(msp_isotopicData == nullptr || msp_isotopicData.get() == nullptr) { if(error_list_p != nullptr) error_list_p->push_back("The IsotopicData are not available."); return false; } if(m_name.isEmpty()) { if(error_list_p != nullptr) error_list_p->push_back("The PolChemDef name is empty."); return false; } if(m_xmlDataFilePath.isEmpty()) { if(error_list_p != nullptr) error_list_p->push_back("The PolChemDef file path is empty."); return false; } file_info.setFile(m_xmlDataFilePath); if(!file_info.exists()) { if(error_list_p != nullptr) error_list_p->push_back("The PolChemDef file cannot be found."); return false; } if(!m_leftCap.validate(msp_isotopicData, error_list_p)) { if(error_list_p != nullptr) error_list_p->push_back("The left cap formula failed to validate."); return false; } if(!m_rightCap.validate(msp_isotopicData, error_list_p)) { if(error_list_p != nullptr) error_list_p->push_back("The right cap formula failed to validate."); return false; } if(m_codeLength < 1) { if(error_list_p != nullptr) error_list_p->push_back("The code length is less than 1."); return false; } if(!m_ionizer.validate(error_list_p)) { if(error_list_p != nullptr) error_list_p->push_back("The ionization rule failed to validate."); return false; } if(!m_modifs.size()) { if(error_list_p != nullptr) error_list_p->push_back("There is not a single Modif instance."); return false; } if(!m_monomers.size()) { if(error_list_p != nullptr) error_list_p->push_back("There is not a single Monomer instance."); return false; } // Good! m_isValid = true; return m_isValid; } /*! \brief Returns the validity status of thie PolChemDef instance. */ bool PolChemDef::isValid() const { return m_isValid; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/PolChemDefSpec.cpp000664 001750 001750 00000007242 15100504560 025555 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/PolChemDefSpec.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::PolChemDefSpec \inmodule libXpertMassCore \ingroup PolChemDef \inheaderfile PolChemDefSpec.hpp \brief The PolChemDefSpec class provides metadata for accessing a given polymer chemistry definition's data on disk. \sa PolChemDef */ /*! \variable MsXpS::libXpertMassCore::PolChemDefSpec::m_name \brief The name of the polymer chemistry definition, like \e{protein-1-letter} or \e{nucac}, for example. This name is typically identical to both the name of the directory where all the data defining this \c PolChemDef is stored and the name of the XML file that contains the definition itself. */ /*! \variable MsXpS::libXpertMassCore::PolChemDefSpec::m_filePath \brief The path to the polymer chemistry defintion. The path is relative to the polymer chemistry definitions directory in the data directory, as found in the catalogue files, like protein-1-letter/protein-1-letter.xml. */ /*! \brief Constructs a polymer chemistry definition specification instance using \a name as the PolChemDef name. */ PolChemDefSpec::PolChemDefSpec(const QString &name) : m_name(name) { } /*! \brief Destroys the polymer chemistry definition specification instance. */ PolChemDefSpec::~PolChemDefSpec() { } /*! \brief Sets the \a name of the polymer chemistry definition. */ void PolChemDefSpec::setName(const QString &name) { m_name = name; } /*! \brief Returns the name of the polymer chemistry definition. */ const QString & PolChemDefSpec::getName() const { return m_name; } /*! \brief Sets the path to the polymer chemistry definition file to \a file_path. */ void PolChemDefSpec::setFilePath(const QString &file_path) { m_filePath = file_path; } /*! \brief Returns the path to the polymer chemistry definition file. */ QString PolChemDefSpec::getFilePath() const { return m_filePath; } // GCOV_EXCL_START /*! \brief Returns the absolute path to the polymer chemistry definition file. If m_filePath is "protein-1-letter/protein-1-letter.xml", returns "/protein-1-letter" */ QString PolChemDefSpec::dirPath() { QFileInfo fileInfo(m_filePath); QDir dir(fileInfo.dir()); return dir.absolutePath(); } // GCOV_EXCL_STOP } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Polymer.cpp000664 001750 001750 00000343421 15100504560 024425 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// stdlib includes /////////////////////// Qt includes #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Polymer.hpp" #include "MsXpS/libXpertMassCore/Modif.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/CrossLink.hpp" int polymerMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::Polymer"); int polymerSPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::PolymerSPtr"); int PolymerCstQSPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::PolymerCstQSPtr"); namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Polymer \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Polymer.hpp \brief The Polymer class provides abstractions to work with a polymer molecule (protein or saccharide , for example). The Polymer class provides a polymer sequence. In the protein world, a polymer sequence is a protein. From a computing standpoint, that sequence is first created by chaining amino acid residues (the residue chain). In a second step, the entity is set to a finished polymer state by adding the N-terminal proton and the C-terminal hydroxyl residue. It might happen also that the ends of a polymer be chemically modified (acetylation of the N-terminal residue, for example). The Polymer class allows modelling a polymer sequence in its finest details. \note As a design decision, Polymer instances can only be created as shared pointer instances. Hence, the constructor is private and is called by \l{createSPtr()}. \note The Polymer class derives from std::enable_shared_from_this because in determinate situations it is necessary to access the shared pointer from the raw pointer (in the particular case of CrossLink instances that need to reference the Polymer in which they occur). */ /*! \variable MsXpS::libXpertMassCore::POL_SEQ_FILE_FORMAT_VERSION \brief Version number of the polymer sequence file format. */ /*! \variable MsXpS::libXpertMassCore::Polymer::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition that is the context in which the Polymer exists. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_name \brief The name of the polymer, for example, "Apomyoglobin". */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_code \brief The code of the polymer, for example, the accession number in SwissProt. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_author \brief The author or creator of the file. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_filePath \brief The file path to the Polymer sequence file. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_dateTime \brief The date and time of last modification. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_sequence \brief The Sequence instance that enshrines the actual Polymer sequence of Monomer instances. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_leftEndModif \brief The left end modification. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_rightEndModif \brief The right end modification. */ /*! \variable MsXpS::libXpertMassCore::Polymer::mp_calcOptions \brief The CalcOptions instance that configure the way masses and formulas are computed. */ /*! \variable MsXpS::libXpertMassCore::Polymer::mp_ionizer \brief The Ionizer instance that configure the ionization of this Polymer instance. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_crossLinks \brief The container of \l UuidCrossLinkWPtrPair instances occurring in this Polymer sequence. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_mono \brief The monoisotopic mass computed for this Polymer instance. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_avg \brief The average mass computed for this Polymer instance. */ /*! \variable MsXpS::libXpertMassCore::Polymer::m_isValid \brief The validity status of this Polymer instance. */ /*! \typedef MsXpS::libXpertMassCore::PolymerSPtr \relates Polymer Synonym for std::shared_ptr. */ /*! \typedef MsXpS::libXpertMassCore::PolymerCstQSPtr \relates Polymer Synonym for std::shared_ptr. */ /*! \brief Constructs a polymer with a number of arguments. \list \li \a pol_chem_def_csp The polymer chemistry definition context in which this Polymer instance exists \li \a name The name of the Polymer instance (could be the name of the protein from the SwissProt or the GenBank database, for example) \li \a code The code of Polymer sequence (could be an accession number from the SwissProt or the GenBank database, for example) \li \a author The author creating the Polymer sequence \li \a sequence_string The monomer codes that make the Polymer sequence. \endlist This function call the actual private constructor. */ PolymerQSPtr Polymer::createSPtr(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &code, const QString &author, const QString &sequence_string) { Polymer *polymer_p = new Polymer(pol_chem_def_csp, name, code, author, sequence_string); return QSharedPointer(polymer_p); } // This constructor is private on purpose, so that a Polymer can only be // constructed as a QSharedPointer using the public createSPtr() above. // See how mp_calcOptions and mp_ionizer can never be nullptr at construction // time. Polymer::Polymer(PolChemDefCstSPtr pol_chem_def_csp, const QString &name, const QString &code, const QString &author, const QString &sequence_string) : mcsp_polChemDef(pol_chem_def_csp), m_name(name), m_code(code), m_author(author), m_sequence(Sequence(pol_chem_def_csp, sequence_string)), mp_calcOptions(new CalcOptions(this)), mp_ionizer(new Ionizer(this)) { m_leftEndModif.setPolChemDefCstSPtr(pol_chem_def_csp); m_rightEndModif.setPolChemDefCstSPtr(pol_chem_def_csp); } /*! \brief Destructs the polymer. */ Polymer::~Polymer() { m_crossLinks.clear(); } //////////////// THE SHARED POINTER ///////////////////// /*! \brief Returns a const-cast of the PolymerSPtr (std::shared_ptr) that manages this Polymer *. This function should never fail because a Polymer can only be allocated as a PolymerSPtr (private Polymer::Polymer() constructor called by a public createSPtr() function). \sa CrossLink::CrossLink(), renderXmlCrossLinksElement() */ PolymerCstQSPtr Polymer::getCstSharedPtr() { PolymerCstQSPtr polymer_cqsp = nullptr; try { polymer_cqsp = sharedFromThis(); } catch(const std::exception &e) { QString message = QString("Exception caught: %1\n%2.") .arg(e.what()) .arg( "Programming error. The pointer cannot be nullptr. If that " "pointer was gotten from Polymer::getCstSharedPtr(), ensure that " "the used raw pointer is indeed managed by a " "QSharedPointer."); qFatalStream().noquote() << message; } return polymer_cqsp; } /*! \brief Returns the PolymerSPtr (std::shared_ptr) that manages this Polymer *. This function should never fail because a Polymer can only be allocated as a PolymerSPtr (private Polymer::Polymer() constructor called by a public createSPtr() function). \sa CrossLink::CrossLink(), renderXmlCrossLinksElement() */ PolymerQSPtr Polymer::getSharedPtr() { PolymerQSPtr polymer_qsp = nullptr; try { polymer_qsp = sharedFromThis(); } catch(const std::exception &e) { QString message = QString("Exception caught: %1\n%2.") .arg(e.what()) .arg( "Programming error. The pointer cannot be nullptr. If that " "pointer was gotten from Polymer::getCstSharedPtr(), ensure that " "the used raw pointer is indeed managed by a " "QSharedPointer."); qFatalStream().noquote() << message; } return polymer_qsp; } //////////////// THE POLCHEMDEF ///////////////////// /*! \brief Sets the polymer chemistry definition to \a pol_chem_def_csp. */ void Polymer::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; ErrorList error_list; m_isValid = validate(&error_list); m_leftEndModif.setPolChemDefCstSPtr(pol_chem_def_csp); m_rightEndModif.setPolChemDefCstSPtr(pol_chem_def_csp); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the polymer chemistry definition. */ const PolChemDefCstSPtr & Polymer::getPolChemDefCstSPtr() const { return mcsp_polChemDef; } //////////////// THE NAME ///////////////////// /*! \brief Sets the \a name. */ void Polymer::setName(const QString &name) { m_name = name; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the name. */ QString Polymer::getName() const { return m_name; } /*! \brief Sets the \a code. The code might identify the polymer in a database, like the Swissprot code, for example. */ void Polymer::setCode(const QString &code) { m_code = code; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the code. */ QString Polymer::getCode() const { return m_code; } /*! \brief Sets the \a author. */ void Polymer::setAuthor(const QString &author) { m_author = author; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the author. */ QString Polymer::getAuthor() const { return m_author; } /*! \brief Sets the polymer's \l{Sequence} file path to \a file_path. */ void Polymer::setFilePath(const QString &file_path) { m_filePath = file_path; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the polymer sequence file path. */ QString Polymer::getFilePath() const { return m_filePath; } /*! \brief Sets the date and time to \a date_time. */ void Polymer::setDateTime(const QString &date_time) { m_dateTime = QDateTime::fromString(date_time, "yyyy-MM-dd:mm:ss"); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Sets the date and time to now. */ void Polymer::setDateTime() { m_dateTime = QDateTime::currentDateTime(); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns the date and time. */ QString Polymer::getDateTime() const { return m_dateTime.toString("yyyy-MM-dd:mm:ss"); } //////////////// THE SEQUENCE ///////////////////// /*! \brief Sets the \l{Sequence} as created using text \a sequence_string. */ void Polymer::setSequence(const QString &sequence_string) { m_sequence = Sequence(mcsp_polChemDef, sequence_string); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Sets the \l{Sequence} as \a sequence. */ void Polymer::setSequence(const Sequence &sequence) { m_sequence = sequence; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); } /*! \brief Returns a const reference to the member Sequence instance. */ const Sequence & Polymer::getSequenceCstRef() const { return m_sequence; } /*! \brief Returns a reference to the member Sequence instance. */ Sequence & Polymer::getSequenceRef() { return m_sequence; } /*! \brief Returns this Polymer instance's \l{Sequence}'s sequence as a text string. */ QString Polymer::getSequenceText() const { return m_sequence.getSequence(); } /*! \brief Returns the size of this Polymer instance as the count of Monomer instances contained in the Monomer container belonging to the member Sequence instance. */ std::size_t Polymer::size() const { return m_sequence.size(); } // MONOMER MODIFICATIONS ///////////////////////////// /*! \brief Returns the count of modified Monomers occurring in the IndexRange instances contained in \a index_range_collection. */ std::size_t Polymer::modifiedMonomerCount( const IndexRangeCollection &index_range_collection) const { qsizetype count = 0; foreach(const IndexRange *item, index_range_collection.getRangesCstRef()) { for(qsizetype iter = item->m_start; iter < item->m_stop + 1; ++iter) { if(m_sequence.getMonomerCstSPtrAt(iter)->isModified()) ++count; } } return count; } /*! \brief Returns true if the modification of Monomer instance at \a index using \a modif_name was succesful, false otherwise. If \a override is true, the Monomer at \a index is modified even if it is current modified the maximum count for \a modif_name. \sa Modif::m_maxCount */ bool Polymer::modifyMonomer(std::size_t index, const QString modif_name, bool override) { // qDebug() << "The modification name:" << modif_name; if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; return m_sequence.modifyMonomer(index, modif_name, override); } /*! \brief Return true if this Polymer's \l{Sequence} contains at least one modified \l{Monomer} in the sequence range delimited by \a left_index and \a right_index, false otherwise. */ bool Polymer::hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const { return m_sequence.hasModifiedMonomer(left_index, right_index); } // POLYMER MODIFICATIONS ///////////////////////////// // ANY END /*! \brief Removes this Polymer instance's modification on the end specified in \a polymer_end. */ void Polymer::unmodify(Enums::PolymerEnd polymer_end) { if(static_cast(polymer_end) & static_cast(Enums::PolymerEnd::LEFT)) setLeftEndModifByName(""); if(static_cast(polymer_end) & static_cast(Enums::PolymerEnd::RIGHT)) setRightEndModifByName(""); } // LEFT END /*! \brief Sets this polymer's left end modification to \a name. If \a name is empty, the corresponding Modif instance of this Polymer object is removed (ie, the polymer's end is unmodifed.) Returns true if a modification by \a name was found in the member polymer chemistry definition's list of modifications and if that modification could be set and its masses could be calculated successfully, otherwise returns false. After setting the member data, the instance is validated and the result is set to m_isValid. */ bool Polymer::setLeftEndModifByName(const QString &name) { if(name.isNull() || name.isEmpty()) { // Reset the modif to nothing. m_leftEndModif.clear(); } if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "Cannot search for Modif name if no PolChemDef is available."; return false; } ModifCstSPtr modif_csp = mcsp_polChemDef->getModifCstSPtrByName(name); if(modif_csp == nullptr) { qCritical() << "Failed to find a Modif by name " << name << "in the polChemDef."; return false; } m_leftEndModif = *modif_csp.get(); if(m_leftEndModif.getPolChemDefCstSPtr() != mcsp_polChemDef) qFatalStream() << "Programming error. The pointers to PolChemDef cannot differ."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); return true; } /*! \brief Sets this polymer's left end modification to \a modif. Returns true if \a modif could validate successfully and if that modification could be set and its masses could be calculated successfully, otherwise returns false. After setting the member data, the instance is validated and the result is set to m_isValid. */ bool Polymer::setLeftEndModif(const Modif &modif) { ErrorList error_list; m_leftEndModif = modif; m_leftEndModif.setPolChemDefCstSPtr(mcsp_polChemDef); if(!m_leftEndModif.validate(&error_list)) { qCritical() << "The Modif is not valid, with errors:" << Utils::joinErrorList(error_list, ", "); return false; } m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); return true; } /*! \brief Returns a const reference to this Polymer's left end modif. */ const Modif & Polymer::getLeftEndModifCstRef() const { return m_leftEndModif; } /*! \brief Returns a reference to this Polymer's left end modif. */ Modif & Polymer::getLeftEndModifRef() { return m_leftEndModif; } /*! \brief Returns true if this Polymer is modified at its left end. */ bool Polymer::isLeftEndModified() const { return m_leftEndModif.isValid(); } // RIGHT END /*! \brief Sets this polymer's right end modification to \a name. Returns true if a modification by \a name was found in the member polymer chemistry definition's list of modifications and if that modification could be set and its masses could be calculated successfully, otherwise returns false. After setting the member data, the instance is validated and the result is set to m_isValid. */ bool Polymer::setRightEndModifByName(const QString &name) { if(name.isNull() || name.isEmpty()) { // Reset the modif to nothing. m_rightEndModif.clear(); } if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "Cannot search for Modif name if no PolChemDef is available."; return false; } ModifCstSPtr modif_csp = mcsp_polChemDef->getModifCstSPtrByName(name); if(modif_csp == nullptr) { qCritical() << "Failed to find a Modif by name " << name << "in the polChemDef."; return false; } m_rightEndModif = *modif_csp.get(); if(m_rightEndModif.getPolChemDefCstSPtr() != mcsp_polChemDef) qFatalStream() << "Programming error. The pointers to PolChemDef cannot differ."; ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); return true; } /*! \brief Sets this polymer's right end modification to \a modif. Returns true if \a modif could validate successfully and if that modification could be set and its masses could be calculated successfully, otherwise returns false. After setting the member data, the instance is validated and the result is set to m_isValid. */ bool Polymer::setRightEndModif(const Modif &modif) { ErrorList error_list; m_rightEndModif = modif; m_rightEndModif.setPolChemDefCstSPtr(mcsp_polChemDef); if(!m_rightEndModif.validate(&error_list)) { qCritical() << "The Modif is not valid, with errors:" << Utils::joinErrorList(error_list, ", "); return false; } m_isValid = validate(&error_list); if(!m_isValid) qCritical() << "Failed to validate the Polymer with errors:\n" << Utils::joinErrorList(error_list, ", "); return true; } /*! \brief Returns a const reference to this Polymer's right end modif. */ const Modif & Polymer::getRightEndModifCstRef() const { return m_rightEndModif; } /*! \brief Returns a reference to this Polymer's right end modif. */ Modif & Polymer::getRightEndModifRef() { return m_rightEndModif; } /*! \brief Returns true if this Polymer is modified at its right end. */ bool Polymer::isRightEndModified() const { return m_rightEndModif.isValid(); } //////////////// THE CALCULATION OPTIONS ///////////////////// /*! \brief Sets \a calc_options to the member datum. */ void Polymer::setCalcOptions(const CalcOptions &calc_options) { mp_calcOptions->initialize(calc_options); } /*! \brief Sets \a calc_options to the member datum. */ void Polymer::setCalcOptions(const CalcOptions *calc_options) { mp_calcOptions->initialize(*calc_options); } /*! \brief Returns a const reference to the member CalcOptions. */ const CalcOptions & Polymer::getCalcOptionsCstRef() const { return *mp_calcOptions; } /*! \brief Returns a reference to the member CalcOptions. */ const CalcOptions & Polymer::getCalcOptionsRef() { return *mp_calcOptions; } /*! \brief Returns a reference to the member CalcOptions. */ CalcOptions * Polymer::getCalcOptions() { // qDebug() << "Returning calculation options:" << mp_calcOptions->toString(); return mp_calcOptions; } // IONIZATION ///////////////////////////// /*! \brief Sets the member Ionizer instance to \a ionizer_p. Calls the Polymer::setIonizer(const Ionizer &ionizer) by dereferencing the pointer. */ void Polymer::setIonizer(const Ionizer *ionizer_p) { // qDebug() << "Setting ionizer:" << ionizer_p->toString(); setIonizer(*ionizer_p); } /*! \brief Sets the member Ionizer instance to \a ionizer. If presently ionized, this Polymer is first deionized. Then this Polymer undergoes ionization according to the current ionization status of \a ionizer. This is required so that we put ourselves in line with the contents of \a ionizer. */ void Polymer::setIonizer(const Ionizer &ionizer) { // qDebug() << "Setting ionizer:" << ionizer.toString(); // Check if ionizer is valid. ErrorList error_list; if(!ionizer.validate(&error_list)) qFatalStream() << "Programming error. Trying to set a new Ionizer that does not " "validate successfully, with errors:" << Utils::joinErrorList(error_list); if(mp_ionizer->isIonized()) { qInfo() << "Before setting a new ionizer, undergo deionization."; if(deionize() == Enums::IonizationOutcome::FAILED) qFatalStream() << "Programming error. Failed to deionize an ionized analyte."; } mp_ionizer->initialize(ionizer); // We now have to put this Polymer in conformity with the contents of the // new Ionizer. That is, we should ionize this Polymer according to the // current state of mp_ionizer-> Only do that if the current state is ionized, // otherwise the ionizer validation below will fail miserably. if(mp_ionizer->currentStateCharge()) { Ionizer current_state_ionizer = mp_ionizer->makeIonizerWithCurrentStateData(); // Now use that current_state_ionizer to ionize this Polymer without // changing its internal Ionizer instance. if(ionize(current_state_ionizer) == Enums::IonizationOutcome::FAILED) qFatalStream() << "Programming error. Failed to ionize the Polymer."; } // No use to validate because validation does not care of // optional Ionizer. } /*! \brief Returns a const reference to the member Ionizer instance. */ const Ionizer & Polymer::getIonizerCstRef() const { return *mp_ionizer; } /*! \brief Returns a reference to the member Ionizer instance. */ Ionizer & Polymer::getIonizerRef() { return *mp_ionizer; } /*! \brief Returns a reference to the member Ionizer instance. */ Ionizer * Polymer::getIonizer() { return mp_ionizer; } /*! \brief Returns the result of the ionization process. The ionization is performed by the member mp_ionizer and the masses that are updated are the member masses of this Polymer instance. */ Enums::IonizationOutcome Polymer::ionize() { return mp_ionizer->ionize(m_mono, m_avg); } /*! \brief Returns the result of the ionization process using \a ionizer. The \a ionizer instance is used to perform the ionization and the masses that are updated are the member masses of this Polymer instance. \sa Ionizer::ionize() */ Enums::IonizationOutcome Polymer::ionize(const Ionizer &ionizer) { // qDebug() << "The ionizer:" << ionizer.toString(); return ionizer.ionize(m_mono, m_avg); } /*! \brief Returns the result of the deionization process. The new masses, if a change occurred, are updated in the member monoisotopic and average masses. */ Enums::IonizationOutcome Polymer::deionize() { return mp_ionizer->deionize(m_mono, m_avg); } /*! \brief Returns the result of the deionization process using \a ionizer. The new masses, if a change occurred, are updated in the member monoisotopic and average masses. */ Enums::IonizationOutcome Polymer::deionize(const Ionizer &ionizer) { return ionizer.deionize(m_mono, m_avg); } /*! \brief Sets \a mono and \a avg to the masses of an unionized Polymer analyte. If the member Ionizer instance reports that this Polymer analyte is ionized, that Ionizer instance is first used to deionize this Polymer instance into copied monoisotopic and average mass values. Then these values are returned as the molecular masses of this Polymer instance. \note Since no member data were touched, that this function is marked const. Returns the process outcome. */ Enums::IonizationOutcome Polymer::molecularMasses(double &mono, double &avg) const { double temp_mono = m_mono; double temp_avg = m_avg; // If not ionized, returns Enums::IonizationOutcome::UNCHANGED which is not an // error. Enums::IonizationOutcome ionization_outcome = mp_ionizer->deionize(temp_mono, temp_avg); if(ionization_outcome == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to deionize the analyte."; return ionization_outcome; } // The masses of the deionized analyte are its Mr molecular mass. mono = temp_mono; avg = temp_avg; return ionization_outcome; } // CROSS-LINKS ///////////////////////////// /*! \brief Returns the number of CrossLink instances in the member container. */ std::size_t Polymer::crossLinkCount() const { return m_crossLinks.size(); } /*! \brief Returns a const reference to the container of CrossLink instances. */ const std::vector & Polymer::getCrossLinksCstRef() const { return m_crossLinks; } /*! \brief Returns a reference to the container of CrossLink instances. */ std::vector & Polymer::getCrossLinksRef() { return m_crossLinks; } /*! \brief Fills-in the \a indices container with indices of Monomer instances involved in cross-links found to be entirely contained in the \a start_index -- \a stop_index sequence range. f This function iterates in all of this polymer's cross-links and checks, for each cross-link if: \list \li It is fully contained in the indices range [\a start_index, \a stop_index] (the indices of the monomer in between the two cross-linked monomers are added to \a indices) \li It is only partially contained in the range (the \a partials counter is then incremented) \li Not contained at all in the range (nothing is done). \endlist Returns true if at least one cross-link was found to be fully encompassed by the range, false otherwise. \sa crossLinksInRange() */ bool Polymer::crossLinkedMonomersIndicesInRange(std::size_t start_index, std::size_t stop_index, std::vector &indices, std::size_t &partials) const { // qDebug() << "Now determining all the Monomer indices of Monomers involved // in " // "cross-links in the Sequence index range" // << start_index << "-" << stop_index; // We are asked to fill-in a list of the indices of all the // monomers involved in cross-links that link any monomer in the // region delimited by start_index and stop_index. bool one_fully_encompassed_was_found = false; // Iterate in the list of cross-links set to *this polymer. For // each iterated cross-link, check if it is fully encompassed by // the region delimited by [startIndex--endIndex]. If so, get the // indices of the monomers involved in that cross-link and append // these (no duplicates) to the indexList passed as param to the // function. // qDebug() << "Iterating in all the CrossLink instances of the Polymer."; for(const CrossLinkCstSPtr cross_link_csp : m_crossLinks) { if(cross_link_csp == nullptr) qFatalStream() << "Programming error. The CrossLink is not more alive."; // qDebug() << "Iterating in cross-link:" << cross_link_csp->toString(); std::size_t in_cross_link; std::size_t out_cross_link; Enums::CrossLinkEncompassed result = cross_link_csp->isEncompassedByIndexRange( start_index, stop_index, in_cross_link, out_cross_link); if(result == Enums::CrossLinkEncompassed::FULLY) { // qDebug() << "Cross-link:" << cross_link_csp->getCrossLinkerName() // << "is fully encompassed by range:" << start_index << "-" // << stop_index; std::vector temp_indices = cross_link_csp->continuumOfLocationsOfInclusiveSequenceMonomers( Enums::LocationType::INDEX); QString indices_as_text; for(std::size_t index : temp_indices) indices_as_text += QString("%1;").arg(index); // qDebug() << "Found" << temp_indices.size() // << "indices of all the Monomers inclusively contained in " // "the current CrossLink:" // << indices_as_text; // Avoid duplicating indices in the target index // list. This will have the excellent side effect of // condensating into one single region a number of // contained cross-linked regions. For example, from the // Kunitz inhibitor, there are the following cross-links: // 90 -- 187 // 230 -- 280 // 239 -- 263 // 255 -- 276 // 286 -- 336 // 295 -- 319 // 311 -- 332 // Thanks to our strategy below, the cross-links // 230 -- 280 // 239 -- 263 // 255 -- 276 // become contained in one single region: // 230--276. // Same for the cross-links // 286 -- 336 // 295 -- 319 // 311 -- 332 // Which become contained in 286--336 for(const int index : temp_indices) { if(std::find(indices.begin(), indices.end(), index) == indices.end()) indices.push_back(index); } one_fully_encompassed_was_found = true; } else if(result == Enums::CrossLinkEncompassed::PARTIALLY) { ++partials; } } // End of // for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) return one_fully_encompassed_was_found; } /*! \brief Fills-in the \a cross_links CrossLink container with cross-links found to be entirely contained in the \a start_index -- \a stop_index sequence range. This function iterates in all of this polymer's cross-links and checks, for each cross-link if: \list \li It is fully contained in the indices range [\a start_index, \a stop_index] (it is then added to \a cross_links) \li It is only partially contained in the range (the \a partials counter is then incremented) \li Not contained at all in the range (nothing is done). \endlist Returns true if at least one cross-link was found to be fully encompassed by the range, false otherwise. \sa crossLinkedMonomersIndicesInRange() */ bool Polymer::crossLinksInRange(std::size_t start_index, std::size_t stop_index, std::vector &cross_links, std::size_t &partials) const { // We are asked to return a list of all the cross-links that are // fully encompassed by the [start_index--stop_index] region. bool one_fully_encompassed_was_found = false; // Iterate in the list of cross-links set to *this polymer. For // each iterated cross-link, check if it is fully encompassed by // the [start_index--stop_index] range. If so simply add it to the // list of cross-links. for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) { if(cross_link_sp == nullptr) qFatalStream() << "Programming error. The CrossLink is not more alive."; // qDebug() << "Cross-link:" << cross_link_csp; std::size_t in_cross_link; std::size_t out_cross_link; Enums::CrossLinkEncompassed result = cross_link_sp->isEncompassedByIndexRange( start_index, stop_index, in_cross_link, out_cross_link); if(result == Enums::CrossLinkEncompassed::FULLY) { // qDebug() << __FILE__ << __LINE__ // << "Cross-link:" << cross_link_csp // << "is encompassed by region:" // << startIndex << "-" << endIndex; cross_links.push_back(cross_link_sp); one_fully_encompassed_was_found = true; } else if(result == Enums::CrossLinkEncompassed::PARTIALLY) { ++partials; } } return one_fully_encompassed_was_found; } /*! \brief Searches for all the CrossLink instances that involve \a monomer_crp and returns a container with the index of each one of them in the member container of CrossLink instances. */ std::vector Polymer::crossLinkIndicesInvolvingMonomer(MonomerCstRPtr monomer_crp) const { std::vector cross_link_indices; // We will need iter, so use this loop. for(std::size_t iter = 0; iter < m_crossLinks.size(); ++iter) { CrossLinkCstSPtr cross_link_csp = m_crossLinks.at(iter); if(cross_link_csp == nullptr) qFatalStream() << "Programming error. The CrossLink is not more alive."; for(const MonomerCstSPtr &monomer_csp : cross_link_csp->getMonomersCstRef()) { if(monomer_csp.get() == monomer_crp) { cross_link_indices.push_back(iter); } } } return cross_link_indices; } /*! \brief Searches for all the CrossLink instances that involve \a monomer_sp and returns a container with all of them. */ std::vector Polymer::crossLinkIndicesInvolvingMonomer(MonomerSPtr monomer_sp) const { return crossLinkIndicesInvolvingMonomer(monomer_sp.get()); } /*! \brief Performs the actual cross-linking as described in \a cross_link_sp. The chemical representation of the cross-link must have been previously defined in \a cross_link_sp. Upon performing the actual cross-link, a Uuid string is generated to identify that cross-link unambiguously. That string is returned. If the cross-link fails, the empty string is returned. Returns true upon success or false if the CrossLink did not not validate successfully. \note Emits the \c{crossLinkChangedSignal(this)} signal to let interested parties know that the Polymer has seen its cross-linking state changed. */ QString Polymer::crossLink(CrossLinkSPtr cross_link_sp) { if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; // This function must be called once all the members taking part // into the crossLink have been set. ErrorList error_list; if(!cross_link_sp->validate(&error_list)) { qCritical() << "Requesting uncross-linking with invalid cross-link."; return QString(); } // OK, from the perspective of the chemical modification of the // monomers involved in the crosslink, everything is fine. // Now is the moment that we actually perform the crossLink : this // is done simply by adding *this crossLink to the list of // crossLinks that belongs to the polymer. QString uuid = storeCrossLink(cross_link_sp); if(uuid.isEmpty()) qFatalStream() << "Programming error. Failed to cross-link monomers."; // If the crossLink dialog is open, inform it that it can refresh // the data. emit(crossLinkChangedSignal(this)); return uuid; } /*! \brief Undoes the cross-link \a cross_link_sp. Returns true upon success or false if the CrossLink does not validate successfully. \note Emits the \c{crossLinkChangedSignal(this)} signal to let interested parties know that the Polymer has seen its cross-linking state changed. */ bool Polymer::uncrossLink(CrossLinkSPtr cross_link_sp) { if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; ErrorList error_list; if(!cross_link_sp->validate(&error_list)) qFatalStream() << "Requesting uncross-linking with invalid cross-link."; // qDebug() << "The CrossLink validated succesfully. Now removing it."; if(!removeCrossLink(cross_link_sp)) qFatalStream() << "Programming error. Failed to remove CrossLink."; // If the crossLink dialog is open, inform it that it can refresh // the data. emit(crossLinkChangedSignal(this)); return true; } // MONOMER REMOVAL ///////////////////////////// /*! \brief Prepares for the removal of \a monomer_csp from this polymer's sequence. The challenge is that monomers might be involved in cross-links. In that case, removing a Monomer instance that was involved in a cross-link needs preparation: it needs first to be uncross-linked. Returns the count of uncross-links that were performed. \sa uncrossLink() */ std::size_t Polymer::prepareMonomerRemoval(MonomerSPtr monomer_csp) { if(monomer_csp == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; if(!m_crossLinks.size()) { // qDebug() << "There are no cross-links, just return 0."; return 0; } // We are asked to destroy **all** the crossLinks that involve the // 'monomer'. bool ok = false; // std::size_t monomer_index = m_sequence.monomerIndex(monomer_csp, ok); m_sequence.monomerIndex(monomer_csp, ok); if(!ok) qFatalStream() << "Programming error."; // qDebug() << "Preparing Monomer removal of" // << monomer_csp->getCode() // << "at polymer Sequence index:" << monomer_index; // Now process all the CrossLinks in the member container and for each one // that involves the Monomer being removed, just uncrosslink. // We cannot use the simple for(CrossLinkSPtr cross_link_sp : m_crossLinks) // range-based iteration because when we remove one CrossLink, we // invalidate all the iterators used internally to make the loop work. // One strategy is to first store in a vector all the CrossLinks that // are found to involve the Monomer we are removing. std::vector cross_links_to_un_cross_link; // Iterate in the list of crossLinks, and for each cross-link check // if it involves 'monomer_csp'. If so, remove the cross-link from // the container. for(CrossLinkSPtr cross_link_sp : m_crossLinks) { // qDebug() // << "Iterating in the cross-link container in Polymer:" // << cross_link_sp->getCrossLinkerName() // << ". Now check if that " // "cross-link contains the Monomer we are preparing the removal // of."; bool ok = false; cross_link_sp->monomerIndex(monomer_csp, ok); if(ok) { // qDebug() << "Yes, the iterated cross-link does contain the Monomer // " // "we are handling. Adding it to the list of cross-links // " "to un-cross-link."; cross_links_to_un_cross_link.push_back(cross_link_sp); } } std::size_t count_of_cross_links_to_remove = cross_links_to_un_cross_link.size(); // qDebug() << "There are" << count_of_cross_links_to_remove // << "cross-links to uncrosslink because they involved the Monomer // we " // "are preparing the removal of."; // We can now safely iterate in the new container of cross-links to // be un-cross-linked. std::size_t count_of_actually_un_crossed_links = 0; for(CrossLinkSPtr cross_link_sp : cross_links_to_un_cross_link) { // Now un-cross-linking that cross-link uncrossLink(cross_link_sp); // qDebug() << "Done un-cross-linking cross-link."; ++count_of_actually_un_crossed_links; } // qDebug() << "Finished iterating the the vector of cross-links and" // << count_of_actually_un_crossed_links // << "cross-links were un-cross-linked."; // Sanity check if(count_of_cross_links_to_remove != count_of_actually_un_crossed_links) qFatalStream() << "Programming error."; return count_of_actually_un_crossed_links; } /*! \brief Removes the Monomer instance at \a index. An index that is out of bounds is fatal. The monomer is first uncross-linked (if it was). Returns true if the removal was succesful, false otherwise. */ bool Polymer::removeMonomerAt(std::size_t index) { // qDebug() << "Asking to remove Monomer at index" << index; if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; MonomerSPtr monomer_csp = m_sequence.getMonomerCstSPtrAt(index); // qDebug() << "The Monomer being removed at index:" << index // << "is:" << monomer_csp->getName() // << "with modification status:" << monomer_csp->isModified(); prepareMonomerRemoval(monomer_csp); // qDebug() << "Done removing the Monomer."; // qDebug() << "Now asking the Sequence to remove the Monomer."; return m_sequence.removeMonomerAt(index); } // MASS CALCULATION FUNCTIONS /*! \brief Calculates the masses of \a polymer_p. The calculated masses are set to \a mono and \a avg. If \a reset is true, \a mono and \a avg are reset to 0.0, otherwise they are left unchanged and incremented with the masses obtained from the calculation. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. The ionization of the polymer is not accounted for in this function. Returns true if all the calculation steps were performed succesfully, false otherwise. \sa calculateMasses() */ bool Polymer::calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg, bool reset) { if(polymer_p == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; if(reset) { // Reset the masses to 0. mono = 0; avg = 0; } // The calc_options parameter holds a SequenceRanges instance // listing all the sequence_range of the different(if any) region // selections of the polymer sequence. This SequenceRanges is // never empty, as it should at least contain the pseudo-selection // of the sequence, that is [start of sequence, cursor index] or // the [-1, -1] values for whole sequence mass // calculation. Iterate in this SequenceRanges and for each item // call this function. const IndexRangeCollection &index_range_collection = calc_options.getIndexRangeCollectionCstRef(); if(!index_range_collection.size()) { qCritical() << "Calculating masses for a Polymer with no SequenceRange."; return false; } // For each SequenceRange item in the // calc_options.getIndexRangeCollectionCstRef() list of such items, perform // the mass calculation. foreach(const IndexRange *item, index_range_collection.getRangesCstRef()) { IndexRange local_index_range; local_index_range.initialize(*item); // If the end value is greater than the polymer size, set it // to the polymer size. if(local_index_range.m_stop >= (qsizetype)polymer_p->size()) local_index_range.m_stop = polymer_p->size() - 1; // qDebug() << "Iterating in SequenceRange:" // << QString("[%1-%2]") // .arg(local_index_range.start) // .arg(local_index_range.stop); // First account for the residual chain masses. // qDebug() << "calculateMasses: accounting for residual chain indices " // << "[" << item->m_start << "--" << item->m_stop << "]"; for(qsizetype iter = local_index_range.m_start; iter <= local_index_range.m_stop; ++iter) { // qDebug() << "Iterating in range index:" << iter; MonomerSPtr monomer_sp = std::const_pointer_cast( polymer_p->getSequenceCstRef().getMonomerCstSPtrAt(iter)); if(calc_options.isDeepCalculation()) { // qDebug() << "Deep calculation or Enums::ChemicalEntity::MODIF // requested."; monomer_sp->calculateMasses(nullptr, calc_options.getMonomerEntities()); } // qDebug() << "Iterating in monomer:" << monomer_sp->getCode() // << "with masses:" << monomer_sp->getMass(Enums::MassType::MONO) // << "-" << monomer_sp->getMass(Enums::MassType::MONO); monomer_sp->accountMasses(mono, avg); } } // qDebug() << "After having accounted the masses of all the monomers:" << // mono // << "-" << avg; // Even if we are not in the residual chain loop, we have to account // for the crossLinks, if so requires it. The crossLinks are a // monomer chemical entity, but because it is unpractical to // calculate their ponderable contribution in the loop above, we // deal with them here. This is difficult stuff. In fact, the // crossLinks, which in reality belong to at least two monomers //(monomers can be engaged in more than a crossLink), are not // stored as properties in the monomers(contrary to monomer // modifications, for example). The crossLinks are stored in a list // of such instances in the polymer(m_crossLinkList of CrossLink // pointers). Now, the point is: if one of the monomers of a // crossLink is selected but not the other partners, then what // should be do about that crossLink accounting ? if(static_cast(calc_options.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::CROSS_LINKER)) { // qDebug() << "Cross-links are to be accounted for."; // We have to take into account the crossLinks. Hmmm... hard // task. The calculation is to be performed for the sequence // stretch from localStart to localEnd. We can iterate in the // crossLink list and for each crossLink check if it involves // monomers that *all* are contained in the sequence stretch //(or sequence stretches, that is a number of SequenceRange // items in the calc_options.coordinateList()) we're // calculating the mass of. If at least one monomer of any // crossLink is not contained in the [localStart--localEnd] // sequence stretch, than increment a count variable and do // not account the mass. int partial_cross_links_count = 0; for(CrossLinkSPtr cross_link_sp : polymer_p->m_crossLinks) { std::size_t in_count; std::size_t out_count; Enums::CrossLinkEncompassed result = cross_link_sp->isEncompassedByIndexRangeCollection( calc_options.getIndexRangeCollectionCstRef(), in_count, out_count); if(result == Enums::CrossLinkEncompassed::FULLY) { // qDebug() << "CrossLink:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << "is fully encompassed: accounting its masses."; // The cross_link_sp is fully encompassed by our monomer // stretch, so we should take it into account. cross_link_sp->accountMasses(mono, avg); } else if(result == Enums::CrossLinkEncompassed::PARTIALLY) { // qDebug() << "CrossLink:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << "is only partially encompassed: not accounting its" // "masses."; ++partial_cross_links_count; } else { // qDebug() // << "CrossLink:" // << cross_link_sp->getCrossLinkerCstSPtr()->getName() // << "is not encompassed at all: not accounting its masses."; } } emit(polymer_p->crossLinksPartiallyEncompassedSignal( partial_cross_links_count)); } // qDebug() << "After having accounted the masses of all the cross-links:" // << mono << "-" << avg; // We now have to account for the left/right cappings. However, // when there are multiple region selections(that is multiple // Coordinate elements in the calc_options.coordinateList()) it is // necessary to know if the user wants each of these SequenceRange // to be considered real oligomers(each one with its left/right // caps) or as residual chains. Thus there are two cases: // 1. Each SequenceRange item should be considered an oligomer //(Enums::SelectionType is SELECTION_TYPE_OLIGOMERS), thus for each item // the left and right caps should be accounted for. This is // typically the case when the user selects multiple regions to // compute the mass of cross-linked oligomers. // 2. Each SequenceRange item should be considered a residual chain //(Enums::SelectionType is SELECTION_TYPE_RESIDUAL_CHAINS), thus only // one item should see its left and right caps accounted for. This // is typically the case when the user selects multiple regions // like it would select repeated sequence elements in a polymer // sequence: all the regions selected are treated as a single // oligomer. // Holds the number of times the chemical entities are to be // accounted for. int times = 0; if(calc_options.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS) { // qDebug() << __FILE__ << __LINE__ // << "SELECTION_TYPE_RESIDUAL_CHAINS"; times = 1; } else { // qDebug() << __FILE__ << __LINE__ // << "SELECTION_TYPE_OLIGOMERS"; times = calc_options.getIndexRangeCollectionCstRef().size(); } // Account for the left and right cap masses, if so required. if(static_cast(calc_options.getCapType()) & static_cast(Enums::CapType::LEFT)) { bool result = Polymer::accountCappingMasses( polymer_p, Enums::CapType::LEFT, mono, avg, times); Q_ASSERT(result); } if(static_cast(calc_options.getCapType()) & static_cast(Enums::CapType::RIGHT)) { bool result = Polymer::accountCappingMasses( polymer_p, Enums::CapType::RIGHT, mono, avg, times); Q_ASSERT(result); } // Account for the left and right modification masses, if so // required and the region(s) require(s) it: we have to make it // clear if the selection encompasses indices 0(left end) and/or // polymerSize-1(right end). // Note that if we are forced to take into account either or both // the left/right end modif, then even if the selected region does // not encompass the end(s), their modif(s) must be taken into // account. if(static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::LEFT_END_MODIF)) { if(calc_options.getIndexRangeCollectionCstRef().encompassIndex(0) || static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::FORCE_LEFT_END_MODIF)) { // qDebug() << "Before accounting for LEFT_END_MODIF, masses:" << mono // << "-" << avg; Polymer::accountEndModifMasses( polymer_p, Enums::ChemicalEntity::LEFT_END_MODIF, mono, avg); // qDebug() << "After accounting for LEFT_END_MODIF, masses:" << mono // << "-" << avg; } } if(static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::RIGHT_END_MODIF)) { if(calc_options.getIndexRangeCollectionCstRef().encompassIndex( polymer_p->size() - 1) || static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::FORCE_RIGHT_END_MODIF)) { // qDebug() << "Before accounting for RIGHT_END_MODIF, masses:" << // mono // << "-" << avg; Polymer::accountEndModifMasses( polymer_p, Enums::ChemicalEntity::RIGHT_END_MODIF, mono, avg); // qDebug() << "After accounting for RIGHT_END_MODIF, masses:" << mono // << "-" << avg; } } // qDebug() << "CalculateMasses Mono:" << mono << "Avg:" << avg; return true; } /*! \overload calculateMasses() \brief Calculates the masses of \a polymer_p. The calculated masses are set to \a mono and \a avg. Prior to setting the masses to \a mono and \a avg, these masses are reset to 0.0. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. After the masses are computed, the ionization described in \a ionizer is accounted for. Returns true if all steps could be performed successfully, false otherwise. \sa calculateMasses(), CalcOptions, Ionizer */ bool Polymer::calculateMasses(const Polymer *polymer_p, const CalcOptions &calc_options, const Ionizer &ionizer, double &mono, double &avg) { if(!calculateMasses(polymer_p, calc_options, mono, avg, true)) return false; Ionizer local_ionizer_copy(ionizer); // If the ionizer is valid use it. if(local_ionizer_copy.isValid()) { if(local_ionizer_copy.ionize(mono, avg) == Enums::IonizationOutcome::FAILED) { qCritical() << "Failed to ionize the Oligomer."; return false; } } return true; } /*! \overload calculateMasses() \brief Calculates the masses of this polymer. The calculated masses are set to this polymer's m_mono and m_avg members. If \a reset is true, these masses are first reset to 0.0, otherwise this polymer's masses are left unchanged and incremented with those obtained from the calculation. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. No ionization is accounted for in this function. Returns true. \sa calculateMasses(), CalcOptions */ bool Polymer::calculateMasses(const CalcOptions &calc_options, bool reset) { return calculateMasses(this, calc_options, m_mono, m_avg, reset); } /*! \overload calculateMasses() \brief Calculates the masses of this polymer. The calculated masses are set to this polymer's m_mono and m_avg members. These masses are first reset to 0.0. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. After the masses are computed, the ionization described in \a ionizer is accounted for. Returns true if all steps could be performed successfully, false otherwise. \sa calculateMasses(), CalcOptions, Ionizer */ bool Polymer::calculateMasses(const CalcOptions &calc_options, const Ionizer &ionizer) { return calculateMasses(this, calc_options, ionizer, m_mono, m_avg); } /*! \overload calculateMasses() \brief Calculates the masses of this polymer. The calculated masses are set to this polymer's m_mono and m_avg members. These masses are first reset to 0.0. The calculation of the masses (monoisotopic and average) is configured in \a calc_options_p. After the masses are computed, the ionization described in \a ionizer_p is accounted for. Returns true if all steps could be performed successfully, false otherwise. \sa calculateMasses(), CalcOptions, Ionizer */ bool Polymer::calculateMasses(const CalcOptions *calc_options_p, const Ionizer *ionizer_p) { bool result = calculateMasses(this, *calc_options_p, *ionizer_p, m_mono, m_avg); // qDebug() << "After computation of the masses:" << m_mono << "-" // << m_avg; return result; } /*! \overload calculateMasses() \brief Calculates the masses of this polymer. The calculated masses are set to this polymer's m_mono and m_avg members. These masses are first reset to 0.0. The calculation of the masses (monoisotopic and average) is configured in the member CalcOptions object. After the masses are computed, the ionization described in the member Ionizer object is accounted for. Returns true if all steps could be performed successfully, false otherwise. \sa calculateMasses(), CalcOptions, Ionizer */ bool Polymer::calculateMasses() { return calculateMasses(*mp_calcOptions, *mp_ionizer); } /*! \brief Accounts the masses of this polymer. Accounting masses means calculating masses and adding the results to objects. Here the masses of this polymer are calculated and added to those of this polymer. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. Returns true upon success, false otherwise. \sa CalcOptions */ bool Polymer::accountMasses(const CalcOptions &calc_options) { // We do not want to reset masses prior to calculating the masses // because we are accounting them in the polymer itself. return calculateMasses(calc_options, false); } /*! \overload accountMasses() \brief Accounts the masses of \a polymer_p. \a polymer_p cannot be nullptr. Accounting masses means calculating masses and adding the results to objects. Here the masses of this polymer are calculated and set to \a mono and \a avg. The calculation of the masses (monoisotopic and average) is configured in \a calc_options. \sa CalcOptions */ bool Polymer::accountMasses(const Polymer *polymer_p, const CalcOptions &calc_options, double &mono, double &avg) { qDebug().noquote() << "Going to account masses for calculation options:" << calc_options.toString(); // We do not want to reset masses prior to calculating the masses // because we are not accounting them in the polymer itself. return calculateMasses(polymer_p, calc_options, mono, avg, /*reset*/ false); } /*! \brief Accounts for the mass of the end caps. The polymer sequence is actually a chain of monomers (that is, residues). In order to compute the mass of the polymer in its finished state, it is necessary to add the masses of its end caps (typically, a proton and a hydroxyl group in protein chemistry, respectively capping the N-terminus and the C-terminus). The mass of the the left end cap is added to the monoisotopic and average masses of this polymer if (\a cap_type & Enums::CapType::LEFT). The mass of the the right end cap is added to the monoisotopic and average masses of this polymer if (\a cap_type & Enums::CapType::RIGHT). The masses of the caps are multiplied by \a times before accounting them to this polymer's masses. Returns true. */ bool Polymer::accountCappingMasses(Enums::CapType cap_type, int times) { return accountCappingMasses(this, cap_type, m_mono, m_avg, times); } /*! \brief Accounts for the \a{polymer_p}'s end caps masses to \a mono and \a avg. \a polymer_p cannot be nullptr. The polymer sequence is actually a chain of monomers (that is, residues). In order to compute the mass of the polymer in its finished state, it is necessary to add the masses of its end caps (typically, a proton and a hydroxyl group in protein chemistry, respectively capping the N-terminus and the C-terminus). The mass of the the left end cap is added to the \a mono and \a avg masses if (\a cap_type & Enums::CapType::LEFT). The mass of the the right end cap is added to the \a mono and \a avg masses if (\a cap_type & Enums::CapType::RIGHT). The masses of the caps are multiplied by \a times before accounting them to \a mono and \a avg. Returns true. */ bool Polymer::accountCappingMasses(const Polymer *polymer_p, Enums::CapType cap_type, double &mono, double &avg, int times) { if(polymer_p == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; IsotopicDataCstSPtr isotopic_data_csp = polymer_p->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(); Formula formula; if(static_cast(cap_type) & static_cast(Enums::CapType::LEFT)) { formula = polymer_p->getPolChemDefCstSPtr()->getLeftCap(); } else if(static_cast(cap_type) & static_cast(Enums::CapType::RIGHT)) { formula = polymer_p->getPolChemDefCstSPtr()->getRightCap(); } else if(static_cast(cap_type) & static_cast(Enums::CapType::NONE)) return true; else qFatalStream() << "Programming error"; bool ok = false; formula.accountMasses(ok, isotopic_data_csp, mono, avg, times); if(!ok) return false; return true; } /*! \brief Accounts for this Polymer instance's end modifications masses as defined by \a polymer_chem_entities. \sa accountEndModifMasses() */ void Polymer::accountEndModifMasses(Enums::ChemicalEntity polymer_chem_entities) { return accountEndModifMasses(this, polymer_chem_entities, m_mono, m_avg); } /*! \brief Accounts for the \a{polymer_p}'s masses as defined by \a polymer_chem_entities. The end modifications masses are accounted for into the \a mono and \a avg masses without prior resetting of them. If \c{(polymer_chem_entities & Enums::ChemicalEntity::LEFT_END_MODIF)}, the left end modification masses are accounted for; if \c{(polymer_chem_entities & Enums::ChemicalEntity::RIGHT_END_MODIF)} the right end modification masses are accounted for. */ void Polymer::accountEndModifMasses(const Polymer *polymer_p, Enums::ChemicalEntity polymer_chem_entities, double &mono, double &avg) { if(polymer_p == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; // Make a safe copy of the polymer's left/right end modif and use it // for doing the calculation INTO the 'mono' and 'avg' variables. if(static_cast(polymer_chem_entities) & static_cast(Enums::ChemicalEntity::LEFT_END_MODIF)) { Modif modif(polymer_p->getLeftEndModifCstRef()); modif.accountMasses(mono, avg); } if(static_cast(polymer_chem_entities) & static_cast(Enums::ChemicalEntity::RIGHT_END_MODIF)) { Modif modif(polymer_p->getRightEndModifCstRef()); modif.accountMasses(mono, avg); } } /*! \brief Returns the Polymer's mass of the type defined by \a mass_type. */ double Polymer::getMass(Enums::MassType mass_type) const { if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG) qFatalStream() << "The mass_type needs to be either MONO or AVG."; if(mass_type == Enums::MassType::MONO) return m_mono; return m_avg; } /*! \brief Returns a reference to the Polymer's mass of the type defined by \a mass_type. */ double & Polymer::getMassRef(Enums::MassType mass_type) { if(mass_type != Enums::MassType::MONO && mass_type != Enums::MassType::AVG) qFatalStream() << "The mass_type needs to be either MONO or AVG."; if(mass_type == Enums::MassType::MONO) return m_mono; return m_avg; } // ELEMENTAL COMPOSITION //////////////////////////// /*! \brief Determines the elemental composition of this polymer. The elemental composition is computed by looking into the core chemical entities of the polymer, like the monomers, the modifications, but also by accounting for the Ionizer \a ionizer, and the CalcOptions \a calc_options. The sequence elements that are accounted for are described in \l IndexRange instances stored in \a index_range_collection. \a ionizer is checked for validity and if valid, it is accounted for. Returns the elemental composition. \sa elementalComposition(), IndexRange, Ionizer, CalcOptions */ QString Polymer::elementalComposition( const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options, const Ionizer &ionizer) const { IsotopicDataCstSPtr isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr(); // Iterate in all the oligomers that are encompassed in the // IndexRangeCollection. qDebug() << "The index range collection has" << index_range_collection.size() << "IndexRange objects"; Formula formula; foreach(const IndexRange *item, index_range_collection.getRangesCstRef()) { qDebug() << "Iterating into IndexRange (indices):" << item->indicesAsText(); for(qsizetype iter = item->m_start; iter <= item->m_stop; ++iter) { const MonomerSPtr monomer_csp = m_sequence.getMonomerCstSPtrAt(iter); formula.setActionFormula(monomer_csp->getFormula()); if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error. Accounting symbols should not fail " "at this point."; if(static_cast(calc_options.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::MODIF)) { for(const ModifSPtr &modif_sp : monomer_csp->getModifsCstRef()) { qDebug() << "Before accounting for Monomer Modif" << modif_sp->getName() << "elemental composition:" << formula.elementalComposition(); formula.setActionFormula( modif_sp->formula(/*with_title*/ false)); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error. Accounting symbols should " "not fail at this point."; qDebug() << "After accounting for Monomer Modif" << modif_sp->getName() << "elemental composition:" << formula.elementalComposition(); } qDebug() << "After having accounted for monomer:" << monomer_csp->getCode() << "the formula has become:" << formula.elementalComposition(); } } } qDebug() << "Formula after accounting for all the residual chains:" << formula.elementalComposition(); // We now have to account for the left/right cappings. However, // when there are multiple region selections(that is multiple // Coordinate elements in the calc_options.coordinateList()) it is // necessary to know if the user wants each of these SequenceRange // to be considered real oligomers(each one with its left/right // caps) or as residual chains. Thus there are two cases: // 1. Each SequenceRange item should be considered an oligomer //(Enums::SelectionType is SELECTION_TYPE_OLIGOMERS), thus for each item // the left and right caps should be accounted for. This is // typically the case when the user selects multiple regions to // compute the mass of cross-linked oligomers. // 2. Each SequenceRange item should be considered a residual chain //(Enums::SelectionType is SELECTION_TYPE_RESIDUAL_CHAINS), thus only // one item should see its left and right caps accounted for. This // is typically the case when the user selects multiple regions // like it would select repeated sequence elements in a polymer // sequence: all the regions selected are treated as a single // oligomer. // Holds the number of times the chemical entities are to be // accounted for. int times = 0; if(calc_options.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS) { times = 1; // qDebug() << "Enums::SelectionType::RESIDUAL_CHAINS ; times:" << times; } else if(calc_options.getSelectionType() == Enums::SelectionType::OLIGOMERS) { times = index_range_collection.size(); // qDebug() << "Enums::SelectionType::OLIGOMERS ; times:" << times; } else qFatalStream() << "Programming error. Cannot be that this point is reached."; // Account for the left and right cap masses, if so required. if(static_cast(calc_options.getCapType()) & static_cast(Enums::CapType::LEFT)) { formula.setActionFormula(mcsp_polChemDef->getLeftCap()); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, times)) qFatalStream() << "Programming error."; qDebug() << "Formula after accounting left cap:" << formula.elementalComposition(); } if(static_cast(calc_options.getCapType()) & static_cast(Enums::CapType::RIGHT)) { formula.setActionFormula(mcsp_polChemDef->getRightCap()); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, times)) qFatalStream() << "Programming error."; qDebug() << "Formula after accounting right cap:" << formula.elementalComposition(); } // Account for the left and right modification masses, if so // required and the region(s) require(s) it: we have to make it // clear if the selection encompasses indices 0(left end) and/or // polymerSize-1(right end). if(static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::LEFT_END_MODIF)) { if(index_range_collection.encompassIndex(0) || static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::FORCE_LEFT_END_MODIF)) { formula.setActionFormula( m_leftEndModif.formula(/*with_title*/ false)); // qDebug() << "Accounting for left end modif:" // << m_leftEndModif.getName() // << "Formula before accounting it:" // << formula.elementalComposition(); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error."; // qDebug() << "Formula after accounting left end modif:" // << formula.elementalComposition(); } } if(static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::RIGHT_END_MODIF)) { if(index_range_collection.encompassIndex(size() - 1) || static_cast(calc_options.getPolymerEntities()) & static_cast(Enums::ChemicalEntity::FORCE_RIGHT_END_MODIF)) { formula.setActionFormula( m_rightEndModif.formula(/*with_title*/ false)); // qDebug() << "Accounting for right end modif:" // << m_rightEndModif.getName(); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error."; // qDebug() << "Formula after accounting left end modif:" // << formula.elementalComposition(); } } // At this point we should not forget if the user asks to take into // account the cross-links... However, BE CAREFUL that cross-links // can only be taken into account if all the partners of a given // cross-link are actually encompassed into the selection. if(static_cast(calc_options.getMonomerEntities()) & static_cast(Enums::ChemicalEntity::CROSS_LINKER)) { for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) { std::size_t in_count; std::size_t out_count; if(cross_link_sp->isEncompassedByIndexRangeCollection( index_range_collection, in_count, out_count) == Enums::CrossLinkEncompassed::FULLY) { // The crossLink is fully encompassed by our monomer // stretch, so we should take it into account. qDebug() << "Accounting for fully encompassed cross-link:" << cross_link_sp->getCrossLinkerCstSPtr()->getName(); if(!cross_link_sp->getCrossLinkerCstSPtr() ->getFormula() .isEmpty()) { formula.setActionFormula( cross_link_sp->getCrossLinkerCstSPtr()->getFormula()); qDebug() << "Cross-linker formula:" << cross_link_sp->getCrossLinkerCstSPtr()->getFormula(); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error."; } // And now each modification that belongs to the // crosslinker. for(const ModifCstSPtr &modif_csp : cross_link_sp->getCrossLinkerCstSPtr()->getModifsCstRef()) { qDebug() << "Cross-linker's modif formula:" << modif_csp->formula(/*with_title*/ false); formula.setActionFormula( modif_csp->formula(/*with_title*/ false)); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, 1)) qFatalStream() << "Programming error."; } } // End of // if(cross_link_sp->encompassedBy(sequence_ranges) == // Enums::CrossLinkEncompassed::FULLY) } // End of // for (const CrossLinkSPtr &cross_link_sp : m_crossLinks) } // End of // if(static_cast(calc_options.getMonomerEntities()) & // static_cast(Enums::ChemicalEntity::CROSS_LINKER)) // Attention, only account for the Ionizer if it is valid !!! if(ionizer.isValid()) { // The ionizer passed as param. Do not forget to take into account the // level and not the compound (nominalCharge * level)! formula.setActionFormula(ionizer.getFormulaCstRef().getActionFormula()); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, ionizer.getLevel())) qFatalStream() << "Programming error."; qDebug() << "Formula after accounting ionization: " << formula.elementalComposition(); } #if 0 // As of 20250925, we remove this block because this introduced unexpected ionization // if the user of this function has called it on purpose with an invalid ionizer. else if(mp_ionizer->isValid()) { // The member ionizer. Do not forget to take into account the // level and not the compound (nominalCharge * level)! formula.setActionFormula( mp_ionizer->getFormulaCstRef().getActionFormula()); // Incrementally account for the new formula in the same // atomcount list in the formula. if(!formula.accountSymbolCounts(isotopic_data_csp, mp_ionizer->getLevel())) qFatalStream() << "Programming error."; qDebug() << "Formula after accounting ionization: " << formula.elementalComposition(); } #endif qDebug() << "Returning Formula: " << formula.elementalComposition(); return formula.elementalComposition(); } /*! \ *brief Determines the elemental composition of this polymer. The elemental composition is computed by looking into the core chemical entities of the polymer, like the monomers, the modifications, and the CalcOptions \a calc_options. The sequence elements that are accounted for are described in \l IndexRange instances stored in \a index_range_collection. The member ionizer is checked for validity and if valid is taken into account. Returns the elemental composition. \sa elementalComposition(), IndexRange, Ionizer, CalcOptions */ QString Polymer::elementalComposition( const IndexRangeCollection &index_range_collection, const CalcOptions &calc_options) const { return elementalComposition( index_range_collection, calc_options, *mp_ionizer); } /*! \overload \brief Determines the elemental composition of this polymer. The elemental composition is computed by looking into the core chemical entities of the polymer, like the monomers, the modifications, but also by accounting for the \l Ionizer \a ionizer, and the \l CalcOptions \a calc_options. The IndexRange instances of the Polymer for which the elemental composition is to be computed are found in the IndexRangeCollection instance of \a calc_options. Returns the elemental composition. \sa elementalComposition() */ QString Polymer::elementalComposition(const CalcOptions &calc_options, const Ionizer &ionizer) const { return elementalComposition( calc_options.getIndexRangeCollectionCstRef(), calc_options, ionizer); } /*! \overload \brief Determines the elemental composition of this polymer. The elemental composition is computed by looking into the core chemical entities of the polymer, like the monomers, the modifications, but also by accounting for the \l CalcOptions \a calc_options. The IndexRange instances of the Polymer for which the elemental composition is to be computed are found in the IndexRangeCollection instance of \a calc_options. If the member ionizer is valid, it is accounted for. Returns the elemental composition. \sa elementalComposition() */ QString Polymer::elementalComposition(const CalcOptions &calc_options) const { return elementalComposition( calc_options.getIndexRangeCollectionCstRef(), calc_options, *mp_ionizer); } /*! \brief Parses the XML \a element representing a sequence of monomer codes. We are getting this: \c{MEFEEGTEEDWYGTEEDWYGTEEDWYGTEEDWYGT} about which we need to create \l{Monomer}s and add them to this polymer's \l{Sequence}. Returns true if parsing and conversion of the text to a monomer list were successful, false otherwise. \sa \sa elementalComposition(), Sequence::makeMonomers() */ bool Polymer::renderXmlCodesElement(const QDomElement &element) { QString sequence; // We are getting this: // MEFEEDWYGEEDWYGTEEDWYGTEEDWYGTEEDWYGTEEDWYGTEEDWYGT // We have to make monomers and add them to the list of monomers. if(element.tagName() != "codes") { qCritical() << "Expected element not found."; return false; } std::vector failing_indices; qDebug() << "Now appending the sequence:" << element.text(); if(m_sequence.appendSequence(element.text(), failing_indices) == -1) { qCritical() << "The obtained sequence could not translate into full " "Monomer instances:" << element.text(); return false; } return true; } /*! \brief Extracts the name of the polymer chemistry definition from the \a file_path polymer sequence file. Returns the polymer chemistry definition name. */ QString Polymer::xmlPolymerFileGetPolChemDefName(const QString &file_path) { QDomDocument doc("polSeqData"); QDomElement element; QDomElement child; QDomElement indentedChild; QFile file(file_path); /* protein ... */ if(!file.open(QIODevice::ReadOnly)) return QString(""); if(!doc.setContent(&file)) { file.close(); return QString(""); } file.close(); element = doc.documentElement(); if(element.tagName() != "polseqdata") { qDebug() << "Polymer sequence file is erroneous\n"; return QString(""); } // child = element.firstChildElement(); if(child.tagName() != "polchemdef_name") return QString(""); return child.text(); } /*! \brief Extracts from \a element, using the proper function (\a version), the polymer end modification. The \a element tag is found in the polymer sequence XML file. If the \a element tag name is \c{le_modif}, the modification name is set to the left end modification of this polymer sequence; if the tag name is \c{re_modif}, the right end of this polymer is modifified. The modifications are then rendered in place. Returns true if no error was encountered, false otherwise. \sa Modif::renderXmlMdfElement() */ bool Polymer::renderXmlPolymerModifElement(const QDomElement &element, [[maybe_unused]] int version) { if(element.tagName() != "le_modif" && element.tagName() != "re_modif") { qCritical() << "The element has not the expected 'le_modif' or 're_modif' tag."; return false; } // Go down to the element. QDomElement child = element.firstChildElement(); if(child.isNull()) return true; bool result = false; if(element.tagName() == "le_modif") result = m_leftEndModif.renderXmlMdfElement(child, version); else if(element.tagName() == "re_modif") result = m_rightEndModif.renderXmlMdfElement(child, version); if(!result) qCritical() << "Failed to render one end modifications."; return result; } /*! \brief Extracts from \a element, using the proper function (\a version), all the \l{CrossLink}s contained in it. Each cross-link is rendered apart and applied to this polymer. Returns true if no error was encountered, false otherwise. \sa crossLink() */ bool Polymer::renderXmlCrossLinksElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; QDomElement indentedChild; // element is // // // DisulfideBond // ;2;6; // // if(element.tagName() != "crosslinks") return false; child = element.firstChildElement(); // There can be any number of elements. while(!child.isNull()) { if(child.tagName() != "crosslink") return false; indentedChild = child.firstChildElement(); if(indentedChild.tagName() != "name") return false; // We actually do have a element, so we can allocate // one now. // qDebug() << "Rendering a polymer sequence CrossLink by name:" //<< indentedChild.text(); CrossLinkerCstSPtr cross_linker_csp = mcsp_polChemDef->getCrossLinkerCstSPtrByName(indentedChild.text()); if(cross_linker_csp == nullptr) return false; CrossLinkSPtr cross_link_sp = std::make_shared(cross_linker_csp, getCstSharedPtr()); indentedChild = indentedChild.nextSiblingElement(); if(indentedChild.tagName() != "targets") { cross_link_sp.reset(); return false; } bool ok = false; cross_link_sp->fillInMonomers(indentedChild.text(), ok); if(!ok) { cross_link_sp.reset(); return false; } indentedChild = indentedChild.nextSiblingElement(); if(!indentedChild.isNull()) { if(indentedChild.tagName() != "comment") { cross_link_sp.reset(); return false; } if(!indentedChild.text().isEmpty()) cross_link_sp->setComment(indentedChild.text()); } // At this point the crossLink element is finished rendering, // all we have to do is perform the crossLink proper. if(crossLink(cross_link_sp).isEmpty()) { cross_link_sp.reset(); return false; } child = child.nextSiblingElement(); } return true; } /*! \brief Parses the \a file_path polymer sequence file. During parsing, the encountered data are set to this polymer. This parsing is called "rendering". Returns true if parsing succeeded, false otherwise. */ bool Polymer::renderXmlPolymerFile(const QString &file_path) { qDebug() << "Starting the rendering of the XmlPolymerfile:" << file_path; QString localFilePath; QDomDocument doc("polSeqData"); QDomElement element; QDomElement child; QDomElement indentedChild; /* protein Sample SP2003 rusconi 1967-09-224:09:23 */ if(file_path.isEmpty()) localFilePath = m_filePath; else localFilePath = file_path; QFile file(localFilePath); if(!file.open(QIODevice::ReadOnly)) { qDebug() << "Could not open file."; return false; } if(!doc.setContent(&file)) { qDebug() << "Failed to set file contents to doc object."; file.close(); return false; } file.close(); element = doc.documentElement(); if(element.tagName() != "polseqdata") { qDebug() << "Polymer sequence file is erroneous\n"; return false; } /////////////////////////////////////////////// // Check the version of the document. QString text; if(!element.hasAttribute("version")) text = "1"; else text = element.attribute("version"); bool ok = false; int version = text.toInt(&ok, 10); if(version < 1 || !ok) { qDebug() << "Polymer sequence file has bad version number: " << version; return false; } qDebug() << "The version of the Polymer file:" << version; // child = element.firstChildElement(); if(child.tagName() != "polchemdef_name") return false; // mcsp_polChemDef->setName(child.text()); // child = child.nextSiblingElement(); if(child.tagName() != "name") return false; m_name = child.text(); // child = child.nextSiblingElement(); if(child.tagName() != "code") return false; m_code = child.text(); // child = child.nextSiblingElement(); if(child.tagName() != "author") return false; m_author = child.text(); // child = child.nextSiblingElement(); if(child.tagName() != "datetime") return false; m_dateTime = QDateTime::fromString(child.text(), "yyyy-MM-dd:mm:ss"); // child = child.nextSiblingElement(); if(child.tagName() != "polseq") return false; /* MEFEEDF S MODIF Phosphorylation GRKDKNFLKMGRK Acetylation -H+C2H3O * 1 Phosphorylation -H+H2PO3 * 1 */ qDebug() << "Now starting to render the element."; // There can be any number of and elements, in // whatever order. indentedChild = child.firstChildElement(); while(!indentedChild.isNull()) { if(indentedChild.tagName() == "codes") { qDebug() << "Iterating in element."; if(!renderXmlCodesElement(indentedChild)) { qDebug() << "Failed to render the XML codes element."; return false; } } else if(indentedChild.tagName() == "monomer") { qDebug() << "Iterating in element."; MonomerSPtr monomer_sp = std::make_shared(mcsp_polChemDef); if(!monomer_sp->renderXmlMonomerElement(indentedChild, version)) { qDebug() << "Failed to render the XML monomer element."; monomer_sp.reset(); return false; } m_sequence.storeMonomer(monomer_sp); } else return false; indentedChild = indentedChild.nextSiblingElement(); } // qDebug() << "The sequence turns out to be:" // << m_sequence.getSequence(0, size() - 1, /*with_modifs*/ true); // Go on to the next element(has to be . QString errors; child = child.nextSiblingElement(); if(child.tagName() != "le_modif") errors += "Expected le_modif element not found.\n"; if(!renderXmlPolymerModifElement(child, version)) errors += "Failed to render the left end modif element.\n"; // Go on to the next element(has to be . child = child.nextSiblingElement(); if(child.tagName() != "re_modif") errors += "Expected re_modif element not found.\n"; if(!renderXmlPolymerModifElement(child, version)) errors += "Failed to render the right end modif element.\n"; // Go on to the next element(has to be . child = child.nextSiblingElement(); if(child.tagName() != "crosslinks") errors += "Expected crosslinks element not found.\n"; if(!renderXmlCrossLinksElement(child, version)) errors += "Failed to render the crosslinks element."; if(!errors.isEmpty()) { qDebug().noquote() << "Rendering of XML file failed with error:" << errors; return false; } setFilePath(localFilePath); // qDebug() << "Finished rendering the XML file, returning true."; return true; } /*! \brief Creates the XML DTD for a polymer sequence file. Returns the DTD in string. */ QString Polymer::formatXmlDtd() { return QString( "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "]>\n"); } /*! \brief Writes this polymer to file. Returns true if successful, false otherwise. */ bool Polymer::writeXmlFile() { QString text; QString indent(" "); // We are asked to send an xml description of the polymer sequence. QFile file(m_filePath); if(!file.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open file" << m_filePath << "for writing."; return false; } QTextStream stream(&file); stream.setEncoding(QStringConverter::Utf8); // The DTD stream << formatXmlDtd(); // Open the element. //"\n" stream << QString("\n") .arg(POL_SEQ_FILE_FORMAT_VERSION); if(mcsp_polChemDef->getName().isEmpty()) qFatalStream() << "Programming error. The PolChemDef's name cannot be empty."; stream << QString("%1%2\n") .arg(indent) .arg(mcsp_polChemDef->getName()); stream << QString("%1%2\n") .arg(indent) .arg(m_name.isEmpty() ? "Not Set" : m_name); stream << QString("%1%2\n") .arg(indent) .arg(m_code.isEmpty() ? "Not Set" : m_code); if(m_author.isEmpty()) qFatalStream() << "Programming error. The Polymer's author cannot be empty."; stream << QString("%1%2\n").arg(indent).arg(m_author); m_dateTime = QDateTime::currentDateTime(); stream << QString("%1%2\n").arg(indent).arg(getDateTime()); stream << formatXmlPolSeqElement(POL_SEQ_FILE_FORMAT_VERSION); // Now deal with the polymer modifications. These are represented as // elements. // Left end modif stream << QString("%1\n").arg(indent); if(m_leftEndModif.isValid()) stream << m_leftEndModif.formatXmlMdfElement(/*offset*/ 1); stream << QString("%1\n").arg(indent); stream << QString("%1\n").arg(indent); if(m_rightEndModif.isValid()) stream << m_rightEndModif.formatXmlMdfElement(/*offset*/ 1); stream << QString("%1\n").arg(indent); stream << formatXmlCrossLinksElement(/*offset*/ 1); // Note that at some point, there might be any number of polymer // elements at this place... // Finally close the polseqdata. stream << QString("\n"); return true; } /*! \brief Formats this polymer's sequence as a string suitable to use as an XML element. This function generates a string holding all the elements pertaining to this polymer' \e sequence (the list of monomers, potentially modified, \e not all the other data). The typical element that is generated in this function looks like this: \code MEFEEDWYGEEDWYGTEEDWYGTEEDWYGTEEDWYGTEEDWYGTEEDWYGT S Phosphorylation * \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. Returns a dynamically allocated string that needs to be freed after use. \sa writeXmlFile() */ QString Polymer::formatXmlPolSeqElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString codesString(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } // At this point, we have to iterate in the sequence. If the // monomers are not modified, then put their codes in a raw, like // "ETGSH", in a element. As soon as a monomer is modified, // whatever the modification --that is, it has a prop object in its // --m_propList, it and its contents should be listed in a detailed // element. text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Iterate in the polymer sequence. for(const MonomerSPtr &monomer_sp : m_sequence.getMonomersCstRef()) { // Check if the monomer_csp is modified. If not, we just append its // code to the elongating codesString, else we use a more // thorough monomer_csp element-parsing function. if(!monomer_sp->isModified()) { codesString += monomer_sp->getCode(); continue; } else { // If something was baking in codesString, then we have to // create the element right now, fill the data in it and // close it before opening one element below. if(!codesString.isEmpty()) { text += QString("%1%2%3") .arg(lead) .arg(codesString) .arg("\n"); codesString.clear(); } text += monomer_sp->formatXmlMonomerElement(newOffset); } } // If something was baking in codesString, then we have to // create the element right now, fill the data in it and // close it before opening one element below. if(!codesString.isEmpty()) { text += QString("%1%2%3").arg(lead).arg(codesString).arg("\n"); codesString.clear(); } // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } /*! \brief Formats an XML element suitable to describe the \c element. Iterates in the cross-link list of this polymer and crafts XML elements describing them. The XML element looks like this: \code DisulfideBond ;2;6; \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. Returns the XML element as a dynamically allocated string. */ QString Polymer::formatXmlCrossLinksElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } // This is the kind of string we have to generate. // // // DisulfideBond // ;2;6; // // // At this point, we have to iterate in the list of crosslinks and // for each crosslink determine what's the crosslinker and which // monomer are actually crosslinked together. text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) { if(cross_link_sp == nullptr) continue; text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1%2\n") .arg(lead) .arg(cross_link_sp->getCrossLinkerCstSPtr()->getName()); // Create the string with all the monomer indices(which are the // targets of the crossLink). text += QString("%1%2\n") .arg(lead) .arg(cross_link_sp->locationsOfOnlyExtremeSequenceMonomersAsText( Enums::LocationType::INDEX)); text += QString("%1%2\n") .arg(lead) .arg(cross_link_sp->getComment()); // Prepare the lead. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); } // Prepare the lead. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } // VALIDATIONS //////////////////////////// /*! \brief Validates the Sequence of this polymer, setting errors as messages in \a error_list_p. Returns true if validation was successful, false, otherwise. \sa Sequence::validate() */ bool Polymer::validate(ErrorList *error_list_p) const { m_isValid = true; qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qCritical() << "The polymer has no available PolChemDef."; error_list_p->push_back("The polymer has no available PolChemDef"); } // Name, Code, Author, FilePath all are not compulsory. // DateTime, LE and RE modifs, Ionizer all are not compulsory. // Sequence has to be non-empty and must be valid. if(!m_sequence.validate(error_list_p)) { qCritical() << "The Sequence of the polymer does not validate."; error_list_p->push_back("The Sequence of the polymer does not validate"); m_isValid = false; } // CrossLinks are not compulsory, but if there, they need to validate // successfully. for(const CrossLinkSPtr &cross_link_sp : m_crossLinks) { if(!cross_link_sp->validate(error_list_p)) { qCritical() << "One cross-link of the polymer does not validate."; error_list_p->push_back( "One cross-link of the polymer does not validate"); m_isValid = false; } } m_isValid = error_list_p->size() > error_count ? false : true; return m_isValid; } /*! \brief Returns the validity status of this instance. */ bool Polymer::isValid() const { return m_isValid; } // UTILITIES //////////////////////////// /*! \brief Computes a MD5SUM has with the data described in \a hash_data_specifs. If hash_data_specifs & HASH_ACCOUNT_SEQUENCE, the sequence is included in the hash computation. If hash_data_specifs & HASH_ACCOUNT_MONOMER_MODIF, the monomer modifications are included in the hash computation. If hash_data_specifs & HASH_ACCOUNT_POLYMER_MODIF, the polymer modifications are include in the hash computation. Returns the hash. */ QByteArray Polymer::md5Sum(int hash_data_specifs) const { // We first need to craft a complete string that encapsulates the // maximum number of information from the polymer sequence (sequence, // modifications in the monomers...) depending on the parameter passed // to the function. QString text; if(static_cast(hash_data_specifs) & static_cast(Enums::HashAccountData::SEQUENCE)) { bool with_mnm_modifs = static_cast(hash_data_specifs) & static_cast(Enums::HashAccountData::MONOMER_MODIF); text += m_sequence.getSequence(0, m_sequence.size(), with_mnm_modifs); } bool with_polymer_modifs = static_cast(hash_data_specifs) & static_cast(Enums::HashAccountData::POLYMER_MODIF); if(with_polymer_modifs) { if(isLeftEndModified()) { text += m_leftEndModif.getFormula(); } if(isRightEndModified()) { text += m_rightEndModif.getFormula(); } } // Now that we have the data string, we can craft the hash itself: QByteArray hash = QCryptographicHash::hash(text.toUtf8(), QCryptographicHash::Md5); return hash; } /*! \brief Stores \a cross_link_sp as a \l UuidCrossLinkWPtrPair in the member container. A Uuid string is computed and associated unambiguously to \a cross_link_sp via a \l UuidCrossLinkWPtrPair object. Returns the Uuid string (a \l QUuid). */ QString Polymer::storeCrossLink(CrossLinkSPtr cross_link_sp) { if(cross_link_sp == nullptr) qFatalStream() << "Cannot be that the pointer is nullptr."; if(hasCrossLink(cross_link_sp) || !getUuidForCrossLink(cross_link_sp).isEmpty()) qFatalStream() << "It is prohibited to store the same CrossLinkSPtr more than once."; // Even if we get a ref to shared_ptr, the reference count increment will // occur. m_crossLinks.push_back(cross_link_sp); QString uuid = QUuid::createUuid().toString(); m_uuidCrossLinkPairs.push_back(UuidCrossLinkWPtrPair(uuid, cross_link_sp)); return uuid; } /*! \brief Returns true if \a cross_link_sp was found in the member container of Monomer instances, false otherwise. */ bool Polymer::hasCrossLink(const CrossLinkSPtr &cross_link_sp) const { if(cross_link_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_crossLinks.cbegin(), m_crossLinks.cend(), [cross_link_sp](const CrossLinkSPtr &the_cross_link_sp) { return the_cross_link_sp == cross_link_sp; }); if(the_iterator_cst == m_crossLinks.cend()) return false; // No sanity checks with getCrossLinkFromUuid() or getUuidForCrossLink() // because that makes circular calls (these functions make sanity // checks by calling this hasMonomer().) return true; } /*! \brief Returns true if \a cross_link_sp was found in the member container of Uuid-CrossLink pairs, false otherwise. */ bool Polymer::hasUuid(const CrossLinkSPtr &cross_link_sp) const { if(cross_link_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidCrossLinkPairs.cbegin(), m_uuidCrossLinkPairs.cend(), [cross_link_sp](const UuidCrossLinkWPtrPair &the_pair) { return the_pair.second.lock() == cross_link_sp; }); if(the_iterator_cst == m_uuidCrossLinkPairs.cend()) return false; // Sanity check if(!hasCrossLink(cross_link_sp)) qFatalStream() << "Inconsistency between m_crossLinks and m_uuidCrossLinkPairs."; return true; } /*! \brief Returns the CrossLinkSPtr that is associated with \a uuid, if it is found in the member container of \l UuidCrossLinkWPtrPair objects. If no such pair is found to hold \a uuid as its first member, then nullptr is returned. \sa getCrossLinkFromUuid() */ CrossLinkSPtr Polymer::getCrossLinkFromUuid(const QString &uuid) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidCrossLinkPairs.cbegin(), m_uuidCrossLinkPairs.cend(), [uuid](const UuidCrossLinkCstWPtrPair &the_pair) { return the_pair.first == uuid; }); if(the_iterator_cst == m_uuidCrossLinkPairs.end()) return nullptr; CrossLinkSPtr cross_link_sp = (*the_iterator_cst).second.lock(); // Sanity check if(!hasCrossLink(cross_link_sp)) qFatalStream() << "Inconsistency between m_crossLinks and m_uuidCrossLinkPairs."; return cross_link_sp; } /*! \brief Returns the UUID string identifying \a cross_link_sp in the member container. If no such CrossLink is found, an empty string is returned. */ QString Polymer::getUuidForCrossLink(const CrossLinkSPtr &cross_link_sp) const { if(cross_link_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidCrossLinkPairs.cbegin(), m_uuidCrossLinkPairs.cend(), [cross_link_sp](const UuidCrossLinkWPtrPair &the_pair) { // Do not query the monomer_sp managed object because it can // be nullptr! return the_pair.second.lock() == cross_link_sp; }); if(the_iterator_cst == m_uuidCrossLinkPairs.cend()) { // Sanity check if(hasCrossLink(cross_link_sp)) qFatalStream() << "Inconsistency between the m_crossLinks and the " "m_uuidCrossLinkPairs vectors."; return QString(); } // Sanity check if(!hasCrossLink(cross_link_sp)) qFatalStream() << "Inconsistency between the m_crossLinks and the " "m_uuidCrossLinkPairs vectors."; return (*the_iterator_cst).first; } /*! \brief Returns the MonomerCstSPtr that is associated with \a uuid, if it is found in any CrossLink belonging to this Polymer. If no such Monomer is found, nullptr is returned. \sa getCrossLinkFromUuid() */ MonomerCstSPtr Polymer::getCrossLinkedMonomerCstSPtrFromUuid(const QString &uuid) const { for(const CrossLinkSPtr &cross_link_csp : m_crossLinks) { MonomerCstSPtr monomer_csp = cross_link_csp->getMonomerForUuid(uuid); if(monomer_csp != nullptr) return monomer_csp; } return nullptr; } std::vector Polymer::getAllCrossLinkUuids() const { std::vector the_uuid_strings; for(const UuidCrossLinkWPtrPair &pair : m_uuidCrossLinkPairs) the_uuid_strings.push_back(pair.first); // Sanity check if(the_uuid_strings.size() != m_crossLinks.size()) qFatalStream() << "Inconsistency between the _s and pairs."; return the_uuid_strings; } /*! \brief Removes the \l UuidCrossLinkWPtrPair object from the member container if the second member of that pair references the same CrossLinkSPtr as that referenced by \a cross_link_wp. If no UuidCrossLinkWPtrPair object matches that criterion, nothing happens. \sa cleanupCrossLinks() */ bool Polymer::removeCrossLink(CrossLinkSPtr cross_link_sp) { if(cross_link_sp == nullptr || cross_link_sp.get() == nullptr) qFatalStream() << "Cannot be that pointer is nullptr."; qDebug() << "Going to remove cross-link:" << cross_link_sp->getCrossLinkerName(); // We will need this anyway. QString uuid = getUuidForCrossLink(cross_link_sp); qDebug() << "The Uuid of the cross-link is:" << uuid; std::vector::const_iterator the_iterator_cst = std::find_if(m_crossLinks.begin(), m_crossLinks.end(), [cross_link_sp](CrossLinkSPtr iter_monomer_sp) { return iter_monomer_sp == cross_link_sp; }); if(the_iterator_cst == m_crossLinks.end()) { qCritical() << "The CrossLinkSPtr was not found in the container."; if(!uuid.isEmpty()) qFatalStream() << "Inconsistency between m_crossLinks and m_uuidCrossLinkPairs."; return false; } // At this point, both containers contain cross_link_sp. qDebug() << "Now effectively erasing the CrossLink from the Polymer " "container of CrossLink instances."; m_crossLinks.erase(the_iterator_cst); qDebug() << "Done."; std::vector::const_iterator the_pair_iterator_cst = std::find_if(m_uuidCrossLinkPairs.cbegin(), m_uuidCrossLinkPairs.cend(), [uuid](const UuidCrossLinkWPtrPair &the_pair) { // Do not query the cross_link_sp managed object because it // can be nullptr! return the_pair.first == uuid; }); if(the_pair_iterator_cst == m_uuidCrossLinkPairs.cend()) qFatalStream() << "Inconsistency between m_crossLinks and m_uuidCrossLinkPairs."; qDebug() << "Now effectively erasing the Uuid/CrossLink pair from the Polymer " "container of Uuid/CrossLink pairs."; m_uuidCrossLinkPairs.erase(the_pair_iterator_cst); qDebug() << "Done."; return true; } /*! \brief Removes the \l UuidCrossLinkWPtrPair object from the member container if the first member of that pair matches \a uuid. If no such pair is found to hold \a uuid as its first member, then nothing happens. \sa cleanupCrossLinks() */ void Polymer::removeCrossLink(const QString &uuid) { std::vector::const_iterator the_pair_iterator_cst = std::find_if(m_uuidCrossLinkPairs.cbegin(), m_uuidCrossLinkPairs.cend(), [uuid](const UuidCrossLinkWPtrPair &the_pair) { // Do not query the cross_link_sp managed object because it // can be nullptr! return the_pair.first == uuid; }); if(the_pair_iterator_cst == m_uuidCrossLinkPairs.cend()) return; CrossLinkSPtr cross_link_sp = (*the_pair_iterator_cst).second.lock(); std::vector::const_iterator the_iterator_cst = std::find_if(m_crossLinks.begin(), m_crossLinks.end(), [cross_link_sp](CrossLinkSPtr iter_monomer_sp) { return iter_monomer_sp == cross_link_sp; }); if(the_iterator_cst == m_crossLinks.end()) qFatalStream() << "Inconsistency between m_crossLinks and m_uuidCrossLinkPairs."; m_crossLinks.erase(the_iterator_cst); } /*! \brief Removes from the member container of \l UuidCrossLinkWPtrPair objects all the items having a CrossLinkWPtr referencing a dead CrossLinkSPtr. */ void Polymer::cleanupCrossLinks() { qDebug() << "At beginning, count of UUID-CrossLink pairs:" << m_uuidCrossLinkPairs.size(); std::vector::iterator the_iterator = m_uuidCrossLinkPairs.begin(); std::vector::iterator the_end_iterator = m_uuidCrossLinkPairs.end(); while(the_iterator != the_end_iterator) { if((*the_iterator).second.expired() || (*the_iterator).second.lock() == nullptr || !hasCrossLink((*the_iterator).second.lock())) the_iterator = m_uuidCrossLinkPairs.erase(the_iterator); else ++the_iterator; } qDebug() << "At end, count of UUID-CrossLink pairs:" << m_uuidCrossLinkPairs.size(); } /*! \brief Returns a string describing the polymer. */ QString Polymer::toString() const { QString text = QString("%1 - %2 - %3 - %4 - %5 - %6 - %7\n") .arg(m_name) .arg(m_code) .arg(m_author) .arg(mcsp_polChemDef->getName()) .arg(m_filePath) .arg(m_leftEndModif.getName()) .arg(m_rightEndModif.getName()); QString codes; for(const MonomerSPtr &monomer_sp : m_sequence.getMonomersCstRef()) codes += monomer_sp->getCode(); text += codes; return text; } /*! \brief Clears all the member data. */ void Polymer::clear() { m_name.clear(); m_code.clear(); m_author.clear(); m_filePath.clear(); m_sequence.clear(); m_leftEndModif.clear(); m_rightEndModif.clear(); m_crossLinks.clear(); m_uuidCrossLinkPairs.clear(); } void Polymer::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register Polymer class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&Polymer::staticMetaObject); engine->globalObject().setProperty("Polymer", jsMetaObject); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Prop.cpp000664 001750 001750 00000054436 15100504560 023723 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Prop.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Prop \inmodule libXpertMassCore \ingroup ThePropSystem \inheaderfile Prop.hpp \brief The Prop class is the abstract base class for a number of specialized properties. Properties are libXpertMassCore' way of extending the capabilities of objects. A property is merely an encapsulation of: \list \li a name(a QString); \li a pointer to data specifically allocated for this property to become the owner of these data (the member data pointer is a void *); \endlist In order to perform tasks in the derived classes using dynamic binding, virtual functions are available to derived classes to perform: \list \li XML string formatting (formatXmlElement()); \li XML element rendering (renderXmlElement()); \li data destruction (deleteData()). \endlist Derived classes should be named according the following scheme: XxxYyyZzzzProp, like StringProp or MonomerProp. The classes that benefit from this Property-based extension mechanism all derive from \l PropListHolder. \sa StringProp, PropListHolder */ /*! \variable int MsXpS::libXpertMassCore::Prop::m_name \brief The name of the property. Initialized to "NOT_SET". */ /*! \variable int MsXpS::libXpertMassCore::Prop::mpa_data \brief The allocated data belonging to this Prop instance. Initialized to nullptr. */ /*! \brief Constructs a Prop instance. */ Prop::Prop() { } /*! \brief Constructs a Prop instance with \a name. */ Prop::Prop(const QString &name) : m_name(name) { } /*! \brief Constructs a Prop instance as a copy of \a other. \note The data are not duplicated. */ Prop::Prop(const Prop &other) : m_name(other.m_name) { // We cannot duplicate the data because we do not know their type. } /*! \brief Destructs this Prop instance. \note No action is taken here. */ Prop::~Prop() { // Do nothing here. } /*! \brief Sets the \a name. */ void Prop::setName(QString &name) { m_name = name; } /*! \brief Returns the name. */ const QString & Prop::name() { return m_name; } /*! \brief Sets the \a data. If mpa_data is non-nullptr, deleteData() is called before assigning \a data to mpa_data. Ownership passes to this Prop instance. */ void Prop::setData(void *data) { if(mpa_data != 0) deleteData(); mpa_data = data; } /*! \brief Returns the data. */ void * Prop::data() const { return mpa_data; } /*! \brief Deletes the data. Nothing is done in this implementation. */ void Prop::deleteData() { // Do nothing here. } /*! \brief Assigns \a other to this Prop instance. The data are not duplicated because their type is not known. */ Prop & Prop::operator=(const Prop &other) { if(&other == this) return *this; m_name = other.m_name; // We cannot duplicate the data because we do not know their type. return *this; } /*! \brief Renders the \a element, with the specified \a version. Nothing is done in this implementation and this function returns false. */ bool Prop::renderXmlElement([[maybe_unused]] const QDomElement &element, [[maybe_unused]] int version) { // Do nothing here. return false; } /*! \brief Formats and returns a string representing this Prop instance. The formatting of the XML element is performed using \a offset and \a indent. Nothing is done in this implementation. Returns an empty string. */ QString Prop::formatXmlElement([[maybe_unused]] int offset, [[maybe_unused]] const QString &indent) { // Do nothing here. return QString(); } //////////////////////// StringProp //////////////////////// //////////////////////// StringProp //////////////////////// /*! \class MsXpS::libXpertMassCore::StringProp \inmodule libXpertMassCore \ingroup ThePropSystem \brief The StringProp class is the specialized class for properties that hold data in the form of string objects. A StringProp property is a simple property in which the data is a pointer to an allocated QString. */ /*! \brief Constructs a StringProp instance. \list \li \a name: The name of the property. \li \a data: The data of the property. \endlist The string passed as \a data is used to dynamically allocate a new string with the same contents and then is set to the member mpa_data pointer. */ StringProp::StringProp(const QString &name, const QString &data) { m_name = name; mpa_data = static_cast(new QString(data)); } /*! \brief Constructs a StringProp instance as a copy of \a other. The member data, if non-, are first deleted. */ StringProp::StringProp(const StringProp &other) : Prop(other) { if(mpa_data != nullptr) deleteData(); if(other.mpa_data != nullptr) { QString *text = static_cast(other.mpa_data); mpa_data = static_cast(new QString(*text)); } else mpa_data = nullptr; } /*! \brief Destructs this StringProp instance. Deletion of the data is delegated to \l deleteData(). */ StringProp::~StringProp() { deleteData(); } /*! \brief Deletes the member data. */ void StringProp::deleteData() { if(mpa_data != nullptr && !static_cast(mpa_data)->isNull()) { delete static_cast(mpa_data); mpa_data = nullptr; } } /*! \brief Assigns \a other to this StringProp instance. Returns a reference to this StringProp instance. */ StringProp & StringProp::operator=(const StringProp &other) { if(&other == this) return *this; Prop::operator=(other); if(mpa_data != nullptr) deleteData(); if(other.mpa_data != nullptr) { QString *text = static_cast(other.mpa_data); mpa_data = static_cast(new QString(*text)); } else mpa_data = nullptr; return *this; } /*! \brief Duplicates this StringProp instance and returns a pointer to it. */ StringProp * StringProp::cloneOut() const { StringProp *new_p = new StringProp(*this); return new_p; } /*! \brief Parses the string-only property XML \a element using a \a{version}ed function. Parses the string-only property XML element passed as argument and for each encountered data(name and data) will set the data to this string-only property (this is called XML rendering). The XML element looks like this: \code MODIF acetylation \endcode Returns true if parsing was successful, false otherwise. */ bool StringProp::renderXmlElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; /* This is what we expect. * * MODIF * acetylation * */ if(element.tagName() != "prop") return false; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); // And now we have to manage the prop objects. child = child.nextSiblingElement(); if(child.isNull()) return false; mpa_data = static_cast(new QString(child.text())); return true; } /*! \brief Formats a string suitable to use as an XML element. Formats a string suitable to be used as an XML element in a polymer sequence file. Typical string-only property elements that might be generated in this function look like this: \code MODIF Phosphorylation COMMENT Phosphorylation is only partial \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. */ QString StringProp::formatXmlElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: * * MODIF * Phosphorylation * * * COMMENT * Phosphorylation is only partial * * * As shown, all the member data of the prop object are simple * strings. The name string is never dynamically allocated, while * the data string is always dynamically allocated. */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); text += QString("%1%2\n") .arg(lead) .arg(*static_cast(mpa_data)); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } //////////////////////// IntProp //////////////////////// //////////////////////// IntProp //////////////////////// /*! \class MsXpS::libXpertMassCore::IntProp \inmodule libXpertMassCore \ingroup ThePropSystem \brief The IntProp class is the specialized class for properties that hold data in the form of integer values. A IntProp property is a simple property in which the data is a pointer to an allocated integer. */ /*! \brief Constructs a IntProp instance. \list \li \a name: The name of the property. \li \a data: The data of the property. \endlist The integer passed as \a data is used to dynamically allocate a new integer with the same contents and then is set to the member mpa_data pointer. */ IntProp::IntProp(const QString &name, int data) : Prop(name) { mpa_data = static_cast(new int(data)); } /*! \brief Constructs a IntProp instance as a copy of \a other. */ IntProp::IntProp(const IntProp &other) : Prop(other) { if(other.mpa_data != nullptr) { int *value = static_cast(other.mpa_data); mpa_data = static_cast(new int(*value)); } } /*! \brief Destructs this IntProp instance. Deletion of the data is delegated to \l deleteData(). */ IntProp::~IntProp() { deleteData(); } /*! \brief Deletes the member data. */ void IntProp::deleteData() { if(mpa_data != nullptr) { delete static_cast(mpa_data); mpa_data = 0; } } /*! \brief Assigns \a other to this IntProp instance. Returns a reference to this IntProp instance. */ IntProp & IntProp::operator=(const IntProp &other) { if(&other == this) return *this; Prop::operator=(other); if(mpa_data != nullptr) deleteData(); if(other.mpa_data != nullptr) { int *value = static_cast(other.mpa_data); mpa_data = static_cast(new int(*value)); } else mpa_data = nullptr; return *this; } /*! \brief Duplicates this IntProp instance and returns a pointer to it. */ IntProp * IntProp::cloneOut() const { IntProp *new_p = new IntProp(*this); return new_p; } /*! \brief Parses a integer property XML \a element using a \a{version}ed function. Parses the integer property XML element passed as argument and for each encountered data (name and data) will set the data to this IntProp instance (this is called XML rendering). Returns true if parsing was successful, false otherwise.) */ bool IntProp::renderXmlElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; /* This is what we expect. * IONIZATION_LEVEL 5 * */ if(element.tagName() != "prop") return false; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); // And now we have to manage the prop objects. child = child.nextSiblingElement(); if(child.isNull()) return false; mpa_data = static_cast(new int(child.text().toInt())); return true; } /*! \brief Formats a string suitable to use as an XML element. Formats a string suitable to be used as an XML element. Typical integer property elements that might be generated in this function look like this: \code IONIZATION_LEVEL 5 \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. Returns a dynamically allocated string that needs to be freed after use. */ QString IntProp::formatXmlElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: * * * SEARCHED_MASS * 1000.234 * * */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); QString value; value = QString::number(*static_cast(mpa_data), 'g', 10); text += QString("%1%2\n").arg(lead).arg(value); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } //////////////////////// DoubleProp //////////////////////// //////////////////////// DoubleProp //////////////////////// /*! \class MsXpS::libXpertMassCore::DoubleProp \inmodule libXpertMassCore \ingroup ThePropSystem \brief The DoubleProp class is the specialized class for properties that hold data in the form of double values. A DoubleProp property is a simple property in which the data is a pointer to an allocated double. */ /*! \brief Constructs a DoubleProp instance. \list \li \a name: The name of the property. \li \a data: The data of the property. \endlist The integer passed as \a data is used to dynamically allocate a new double with the same contents and then is set to the member mpa_data pointer. */ DoubleProp::DoubleProp(const QString &name, double data) : Prop(name) { mpa_data = static_cast(new double(data)); } /*! \brief Constructs a DoubleProp instance as a copy of \a other. */ DoubleProp::DoubleProp(const DoubleProp &other) : Prop(other) { if(other.mpa_data != nullptr) { double *value = static_cast(other.mpa_data); mpa_data = static_cast(new double(*value)); } } /*! \brief Destructs this DoubleProp instance. Deletion of the data is delegated to \l deleteData(). */ DoubleProp::~DoubleProp() { deleteData(); } /*! \brief Deletes the member data. */ void DoubleProp::deleteData() { if(mpa_data) { delete static_cast(mpa_data); mpa_data = 0; } } /*! \brief Assigns \a other to this DoubleProp instance. Returns a reference to this DoubleProp instance. */ DoubleProp & DoubleProp::operator=(const DoubleProp &other) { if(&other == this) return *this; Prop::operator=(other); if(mpa_data != nullptr) deleteData(); if(other.mpa_data != nullptr) { double *value = static_cast(other.mpa_data); mpa_data = static_cast(new double(*value)); } else mpa_data = nullptr; return *this; } /*! \brief Duplicates this DoubleProp instance and returns a pointer to it. */ DoubleProp * DoubleProp::cloneOut() const { DoubleProp *new_p = new DoubleProp(*this); return new_p; } /*! \brief Parses a double property XML \a element using a \a{version}ed function. Parses the double property XML element passed as argument and for each encountered data (name and data) will set the data to this DoubleProp instance (this is called XML rendering). Returns true if parsing was successful, false otherwise.) */ bool DoubleProp::renderXmlElement(const QDomElement &element, [[maybe_unused]] int version) { QDomElement child; /* This is what we expect. * * SEARCHED_MASS * 1000.234 * */ if(element.tagName() != "prop") return false; child = element.firstChildElement("name"); if(child.isNull()) return false; m_name = child.text(); // And now we have to manage the prop objects. child = child.nextSiblingElement(); if(child.isNull()) return false; mpa_data = static_cast(new double(child.text().toDouble())); return true; } /*! \brief Formats a string suitable to use as an XML element. Formats a string suitable to be used as an XML element. Typical double property elements that might be generated in this function look like this: \code SEARCHED_MASS 1000.234 \endcode \a offset times the \a indent string must be used as a lead in the formatting of elements. Returns a dynamically allocated string that needs to be freed after use. */ QString DoubleProp::formatXmlElement(int offset, const QString &indent) { int newOffset; int iter = 0; QString lead(""); QString text; // Prepare the lead. newOffset = offset; while(iter < newOffset) { lead += indent; ++iter; } /* We are willing to create an node that should look like this: * * * SEARCHED_MASS * 1000.234 * * */ text += QString("%1\n").arg(lead); // Prepare the lead. ++newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } // Continue with indented elements. text += QString("%1%2\n").arg(lead).arg(m_name); QString value; value = QString::number(*static_cast(mpa_data), 'g', 10); text += QString("%1%2\n").arg(lead).arg(value); // Prepare the lead for the closing element. --newOffset; lead.clear(); iter = 0; while(iter < newOffset) { lead += indent; ++iter; } text += QString("%1\n").arg(lead); return text; } /////////////////// NoDeletePointerProp /////////////////// /////////////////// NoDeletePointerProp /////////////////// /*! \class MsXpS::libXpertMassCore::NoDeletePointerProp \inmodule libXpertMassCore \ingroup ThePropSystem \brief The NoDeletePointerProp class provides a pointer property. A NoDeletePointerProp property is a simple property in which the data is a pointer to an allocated instance, but which may never be destroyed by the property itself. This property is regarded as a simple "message-containing property". The message is nothing but the name of the property. */ /*! \brief Constructs a NoDeletePointerProp instance. \list \li \a name: The name of the property. \li \a no_delete_data_p: The data of the property. \endlist The member data pointer (\l mpa_data) is assigned \a no_delete_data_p and is not going to be deleted upon destruction of this NoDeletePointerProp instance. */ NoDeletePointerProp::NoDeletePointerProp(const QString &name, void *no_delete_data_p) : Prop(name) { mpa_data = no_delete_data_p; } /*! \brief Constructs a NoDeletePointerProp instance as a copy of \a other. */ NoDeletePointerProp::NoDeletePointerProp(const NoDeletePointerProp &other) : Prop(other) { mpa_data = static_cast(other.mpa_data); } /*! \brief Destructs this NoDeletePointerProp instance. Deletion of the data is delegated to \l deleteData(), that won't delete the data. */ NoDeletePointerProp::~NoDeletePointerProp() { deleteData(); } /*! \brief Does not delete the member data. */ void NoDeletePointerProp::deleteData() { // We do not do anything here. } /*! \brief Assigns \a other to this NoDeletePointerProp instance. Returns a reference to this NoDeletePointerProp instance. */ NoDeletePointerProp & NoDeletePointerProp::operator=(const NoDeletePointerProp &other) { if(&other == this) return *this; Prop::operator=(other); mpa_data = static_cast(other.mpa_data); return *this; } /*! \brief Duplicates this NoDeletePointerProp instance and returns a pointer to it. */ NoDeletePointerProp * NoDeletePointerProp::cloneOut() const { NoDeletePointerProp *new_p = new NoDeletePointerProp(*this); return new_p; } /*! \brief This function is a no-op. The \a element and the \a version parameters are not used. The NoDeletePointerProp class is not used to store or read data to or from files. Returns false if the \a element tag name is not "prop", true otherwise. */ bool NoDeletePointerProp::renderXmlElement(const QDomElement &element, [[maybe_unused]] int version) { if(element.tagName() != "prop") return false; return true; } /*! \brief This function is a no-op. The \a offset and the \a indent parameters are not used. The NoDeletePointerProp class is not used to store or read data to or from files. */ QString NoDeletePointerProp::formatXmlElement(int offset, const QString &indent) { QString text = QString(QObject::tr("%1-This function does not return anything " "interesting-%2") .arg(offset) .arg(indent)); return text; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/PropListHolder.cpp000664 001750 001750 00000013302 15100504560 025700 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/PropListHolder.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::PropListHolder \inmodule libXpertMassCore \ingroup ThePropSystem \inheaderfile PropListHolder.hpp \brief The PropListHolder class is the base class for a number of classes that need storing Prop instances. \sa Modif, Monomer, Oligomer, PkaPhPi */ /*! \variable int MsXpS::libXpertMassCore::PropListHolder::m_propList \brief The m_propList stores pointer to \l Prop instances. */ /*! \brief Constructs a PropListHolder instance. */ PropListHolder::PropListHolder() { } /*! \brief Constructs a PropListHolder instance as a copy of \a other. The Prop instances in the \a other PropListHolder instance's member list of Prop instances are duplicated and stored in this instance using their own copy constructor. */ PropListHolder::PropListHolder(const PropListHolder &other) { for(int iter = 0; iter < other.m_propList.size(); ++iter) { Prop *prop = other.m_propList.at(iter); Q_ASSERT(prop); // qDebug() << "Prop name:" << prop->name(); // Each Prop-derived class produces a derived Prop class. m_propList.append(prop->cloneOut()); } } /*! \brief Destructs this PropListHolder instance. */ PropListHolder::~PropListHolder() { while(!m_propList.isEmpty()) delete(m_propList.takeFirst()); } /*! \brief Assigns \a other to this PropListHolder instance. The Prop instances in the \a other PropListHolder instance's member list of Prop instances are duplicated and stored in this instance using their own copy constructor. Returns a reference to this PropListHolder instance. */ PropListHolder & PropListHolder::operator=(const PropListHolder &other) { if(&other == this) return *this; for(int iter = 0; iter < other.m_propList.size(); ++iter) { Prop *prop = other.m_propList.at(iter); Q_ASSERT(prop); m_propList.append(prop->cloneOut()); } return *this; } /*! \brief Returns a const reference to the member list of Prop instances. */ const QList & PropListHolder::propList() const { return m_propList; } /*! \brief Returns a non-const reference to the member list of Prop instances. */ QList & PropListHolder::propList() { return m_propList; } /*! \brief Searches in the member list of Prop instances a Prop having \a name. If the Prop instance was found and \a index is non-nullptr, its index in the list is set to this parameter. Returns the found Prop instance. */ Prop * PropListHolder::prop(const QString &name, int *index) { for(int iter = 0; iter < m_propList.size(); ++iter) { Prop *localProp = m_propList.at(iter); Q_ASSERT(localProp); if(localProp->name() == name) { if(index) *index = iter; return localProp; } } return 0; } /*! \brief Searches in the member list of Prop instances a Prop having \a name. If the Prop instance was found and \a prop_pp is non-nullptr, its pointer is set to this parameter after dereferencing. Returns the index of the found Prop instance. */ int PropListHolder::propIndex(const QString &name, Prop **prop_pp) { for(int iter = 0; iter < m_propList.size(); ++iter) { Prop *localProp = m_propList.at(iter); Q_ASSERT(localProp); if(localProp->name() == name) { if(prop_pp) *prop_pp = localProp; return iter; } } return -1; } /*! \brief Adds \a prop to the member list of Prop instances. Returns true. */ bool PropListHolder::appendProp(Prop *prop) { m_propList.append(prop); return true; } /*! \brief Removes \a prop from the member list of Prop instances. Returns true if \a prop was removed, false otherwise. */ bool PropListHolder::removeProp(Prop *prop) { if(m_propList.removeAll(prop)) return true; return false; } /*! \brief Removes the Prop instance having \a name from the member list of Prop instances. If more than one Prop instance by the \a name are in the list, only the first encountered Prop instance is removed. Returns true if a Prop instance was removed, false otherwise. */ bool PropListHolder::removeProp(const QString &name) { for(int iter = 0; iter < m_propList.size(); ++iter) { if(m_propList.at(iter)->name() == name) { m_propList.removeAt(iter); return true; } } return false; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Sequence.cpp000664 001750 001750 00000137413 15100504560 024550 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009, ..., 2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ ////////////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Sequence.hpp" #include "MsXpS/libXpertMassCore/PolChemDef.hpp" #include "MsXpS/libXpertMassCore/IndexRangeCollection.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Sequence \inmodule libXpertMassCore \ingroup PolChemDefBuildingdBlocks \inheaderfile Sequence.hpp \brief The Sequence class provides abstractions to work with a simple sequence of \l{Monomer}s. A sequence of monomer is a vector of fully qualified \l{Monomer} instances allocated on the heap. */ /*! \variable MsXpS::libXpertMassCore::Sequence::mcsp_polChemDef \brief The \l PolChemDef polymer chemistry definition that is the context in which the Sequence exists. */ /*! \variable MsXpS::libXpertMassCore::Sequence::m_monomers \brief Vector of allocated \l Monomer instances. */ /*! \variable MsXpS::libXpertMassCore::Sequence::m_isValid \brief The validity status of this Sequence instance. */ /*! \brief Constructs a totally empty Sequence as an invalid object. */ Sequence::Sequence() { } /*! \brief Constructs a Sequence in the context of the \a pol_chem_def_csp polymer chemistry definition using the \a sequence_text representation of a Monomer sequence. The \a sequence_text is a concatenation of monomer codes. That text sequence is immediately converted into Monomer instances using \a pol_chem_def_csp as the reference PolChemDef. The Monomer instances (actually newly allocated Monomer shared pointers) are stored in the member container keeping the order of the Monomer codes in \a sequence_text. If all the Monomer codes in the \a sequence_text were correct, the status of the Sequence is set to valid, otherwise it is set to invalid (query with isValid()). \sa makeMonomers() */ Sequence::Sequence(PolChemDefCstSPtr pol_chem_def_csp, const QString &sequence_text) : mcsp_polChemDef(pol_chem_def_csp) { // qDebug() << "The sequence_text:" << sequence_text; std::vector failing_indices; makeMonomers(sequence_text, /*reset*/ true, failing_indices); if(failing_indices.size()) { QString indices_text; for(std::size_t index : failing_indices) indices_text += QString("%1, ").arg(index); qCritical() << "There were errors making the monomers at the following indices:" << indices_text; } else m_isValid = true; } /*! \brief Constructs this Sequence instance as a copy of \a other. The copying is deep with the Monomer instances in the member container being reinstantiated into this Sequence. */ Sequence::Sequence(const Sequence &other) { mcsp_polChemDef = other.mcsp_polChemDef; for(const MonomerSPtr &monomer_sp : other.m_monomers) storeMonomer(monomer_sp); // qDebug() << "The number of Monomer instances:" << m_monomers.size(); ErrorList error_list; m_isValid = validate(&error_list); if(!m_isValid) { qCritical() << "The copy-constructed Sequence is not valid, with errors:" << Utils::joinErrorList(error_list, ", "); } } /*! \brief Destructs this sequence. */ Sequence::~Sequence() { m_monomers.clear(); } //////////////// POLYMER CHEMISTRY DEFINTIION ///////////////////// /*! \brief Sets the polymer chemistry definition to \a pol_chem_def_csp. */ void Sequence::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp) { mcsp_polChemDef = pol_chem_def_csp; } /*! \brief Returns the polymer chemistry definition. */ PolChemDefCstSPtr Sequence::getPolChemDef() const { return mcsp_polChemDef; } //////////////// MONOMERS TEXT / INSTANCES ///////////////////// /*! \brief Sets the \a sequence of Monomer codes to this Sequence. No verification is performed on \a sequence. The codes are immediately converted into newly allocated Monomer instances appended in order to the member container of Monomer instances. The member Monomer container is first cleared. If there are indices of the \a sequence that failed converting to Monomer instances, they are stored in \a failing_indices. After setting the member data, the instance is validated and the result is set to m_isValid. Returns -1 if an error occurred, or the count of Monomer instances actually set to the sequence. */ int Sequence::setSequence(const QString &sequence, std::vector &failing_indices) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) { qFatalStream() << "Programming error. The PolChemDef pointer is nullptr."; } if(!mcsp_polChemDef->isValid()) { qFatalStream() << "Programming error. The PolChemDef is not valid."; } m_monomers.clear(); m_uuidMonomerPairs.clear(); int result = appendSequence(sequence, failing_indices); ErrorList error_list; m_isValid = validate(&error_list); { qCritical() << "The Sequence is not valid, with errors:" << Utils::joinErrorList(error_list, ", "); } return result; } /*! \brief Appends the \a sequence of Monomer codes to this Sequence. No verification is performed on \a sequence. The codes are immediately converted into newly allocated Monomer instances appended in order to the m_monomers container. If there are indices of the \a sequence that failed converting to Monomer instances, they are stored in \a failing_indices. After setting the member data, the instance is validated and the result is set to m_isValid. Returns -1 if an error occurred, or the count of Monomer instances actually added to the sequence. */ int Sequence::appendSequence(const QString &sequence, std::vector &failing_indices) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr) qFatalStream() << "The PolChemDef pointer is nullptr!"; if(!mcsp_polChemDef->isValid()) qFatalStream() << "The PolChemDef is not valid!"; if(sequence.isEmpty()) return 0; QString unspacified_sequence = unspacifySequence(sequence); int result = makeMonomers(unspacified_sequence, /*reset*/ false, failing_indices); if(result <= 0) { qCritical() << "The sequence could not be converted into Monomer " "instances (error or empty sequence)."; } ErrorList error_list; m_isValid = validate(&error_list); { qCritical() << "The Sequence is not valid, with errors:" << Utils::joinErrorList(error_list, ", "); } return result; } /*! \brief Returns a string with all the codes of the Monomer instances found in the member container concatenated in order. */ QString Sequence::getSequence() const { QString sequence; for(const MonomerSPtr &monomer_sp : m_monomers) sequence += monomer_sp->getCode(); return sequence; } /*! \brief Returns a string with all the codes of the Monomer instances found in the member container concatenated in order. The returned string only contains the sequence of monomer codes for Monomer instances included in the indices range [\a start_index -- \a stop_index] (inclusively) in this Sequence's container of Monomer instances. If \a stop_index is > size(), then it is set to size(). If \a with_modif is true, the Modif instances associated to \l{Monomer}s are also output to the string. The form of the string is, in this case, \code Thr \endcode */ QString Sequence::getSequence(std::size_t start_index, std::size_t stop_index, bool with_modif) const { if(!size()) { return QString(); } if(start_index > stop_index) qFatalStream() << "Programming error, please order the indices: [" << start_index << "-" << stop_index << "]"; // We want the last index to be included in the range. std::size_t local_stop = stop_index + 1; if(local_stop > size()) local_stop = size(); QString text; for(std::size_t iter = start_index; iter < local_stop; ++iter) { MonomerSPtr monomer_sp = m_monomers.at(iter); // qDebug() << "Iterating in Monomer:" << monomer_sp->getCode(); if(with_modif && monomer_sp->isModified()) { for(const ModifSPtr &modif_sp : monomer_sp->getModifsCstRef()) text += QString("%1<%2>") .arg(monomer_sp->getCode()) .arg(modif_sp->getName()); } else text += monomer_sp->getCode(); } return text; } /*! \brief Returns a string with all the codes of the Monomer instances found in the member container concatenated in order. The returned string only contains the sequence of monomer codes for Monomer instances contained in the \l{IndexRange} instances contained in \a index_ranges. If \a with_modif is true, the modification(s) associated to \l{Monomer}s are also output to the string. The form of the string is, in this case, \code Thr \endcode If \a delimited_regions is true, the sequence of Monomer codes belonging to each sequence range will be delimited using the IndexRange positions (not the indices). \sa IndexRange::positionsAsText() */ QString Sequence::getSequence(const IndexRangeCollection &index_ranges, bool with_modif, bool delimited_regions) const { QString text; for(const IndexRange *item : index_ranges.getRangesCstRef()) { QString sequence_string = getSequence(item->m_start, item->m_stop, with_modif); if(delimited_regions) text += QString("Region [%1-%2]: %3\n") .arg(item->m_start + 1) .arg(item->m_stop + 1) .arg(sequence_string); else text += sequence_string; } // Removed because this is unnecessary. // text += QString("\n"); return text; } /*! \brief Allocates all the Monomer instances to describe this Sequence's string representation of monomer codes. This function parses the \a sequence_text Monomer codes string and, for each encountered code, creates a \l Monomer instance and adds it to the member container of Monomer instances. If \a reset is true, the member container of Monomer instances is reset before the work is done. Any error that might occur is stored as the index of the failing Monomer code in the \a failing_indices container. The allocation of each Monomer instance based on its code is performed by looking at the reference Monomer in the member polymer chemistry definition.. Because the m_monomerText member string of Monomer codes does not document any monomer modification, no modifications are handled in this function. Returns the count of Monomer instances set to the list or -1 if an error occurred. */ int Sequence::makeMonomers(const QString &sequence_text, bool reset, std::vector &failing_indices) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || !mcsp_polChemDef->isValid()) { qCritical() << "The PolChemDef pointer is nullptr or not valid!"; m_isValid = false; return -1; } if(reset) m_monomers.clear(); failing_indices.clear(); QString local_sequence_text = unspacifySequence(sequence_text); // qDebug() << "Sequence:" << local_sequence_text; std::size_t index = 0; int ret = -1; QString err; QString code; ret = nextCode(local_sequence_text, code, index, err); while(true) { if(ret < 0) { // There was an error in the parsed code. Store the index. failing_indices.push_back(index); ++index; ret = nextCode(local_sequence_text, code, index, err); continue; } if(ret == 0) break; Monomer monomer(mcsp_polChemDef, "", code); MonomerSPtr pol_chem_def_monomer_csp = mcsp_polChemDef->getMonomerCstSPtrByCode(code); if(pol_chem_def_monomer_csp == nullptr) { qWarning() << "Monomer:" << code << "was not found in the monomer reference list."; failing_indices.push_back(index); ++index; ret = nextCode(local_sequence_text, code, index, err); continue; } else { // Fully initialize the monomer with that found in the PolChemDef. monomer = *pol_chem_def_monomer_csp; storeMonomer(std::make_shared(monomer)); // qDebug() << "The newly created Monomer has masses:" // << monomer.getMass(Enums::MassType::MONO) << "-" // << monomer.getMass(Enums::MassType::AVG); } ++index; ret = nextCode(local_sequence_text, code, index, err); } // End of // while(true) if(failing_indices.size()) return -1; if(ret == -1) return -1; return m_monomers.size(); } /*! \brief Returns a const reference to the Monomer container. */ const std::vector & Sequence::getMonomersCstRef() const { return m_monomers; } /*! \brief Returns a reference to the Monomer container. */ std::vector & Sequence::getMonomersRef() { return m_monomers; } // /*! // \brief Returns a reference to this Sequence's container of \l Monomer // instances. // */ // std::vector & // Sequence::getMonomersRef() // { // return m_monomers; // } //////////////// MONOMER ACCESSING FUNCTIONS ///////////////////// /*! \brief Returns the Monomer instance at \a index in this Sequence's container of Monomer instances as a const raw pointer. An index that is out of bounds is fatal. */ MonomerCstRPtr Sequence::getMonomerCstRPtrAt(std::size_t index) const { if(index >= m_monomers.size()) qFatalStream() << "Index is out of bounds."; return m_monomers.at(index).get(); } /*! \brief Returns the Monomer instance at \a index in this Sequence's container of Monomer instances as a raw pointer. An index that is out of bounds is fatal. */ MonomerRPtr Sequence::getMonomerRPtrAt(std::size_t index) { if(index >= m_monomers.size()) qFatalStream() << "Index is out of bounds."; return m_monomers.at(index).get(); } /*! \brief Returns the Monomer instance at \a index in this Sequence's container of Monomer instances as a const shared pointer. An index that is out of bounds is fatal. */ MonomerSPtr Sequence::getMonomerCstSPtrAt(std::size_t index) const { if(index >= m_monomers.size()) qFatalStream() << "Programming error. Index is out of bounds:" << index << "with monomer count:" << m_monomers.size(); return m_monomers.at(index); } /*! \brief Returns the Monomer instance at \a index in this Sequence's container of Monomer instances as a shared pointer. An index that is out of bounds is fatal. */ MonomerSPtr Sequence::getMonomerSPtrAt(std::size_t index) { if(index >= m_monomers.size()) qFatalStream() << "Index is out of bounds."; return m_monomers.at(index); } /*! \brief Returns the index of \a monomer_sp in this Sequence's container of Monomer instances. The search is based on comparison of the pointers, that is, the returned index is for the \e same Monomer object (pointer-wise). If the Monomer was found, the returned value is certain to be correct and \a ok is set to true. If the Monomer was not found, the returned value is 0 and ok is set to false. */ std::size_t Sequence::monomerIndex(MonomerSPtr monomer_sp, bool &ok) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [monomer_sp](const MonomerSPtr &iter_monomer_sp) { return iter_monomer_sp == monomer_sp; }); if(the_iterator_cst != m_monomers.cend()) { ok = true; return std::distance(m_monomers.cbegin(), the_iterator_cst); } ok = false; return 0; } /*! \brief Returns the index of \a monomer_csp in this Sequence's container of Monomer instances. The search is based on comparison of the pointers, that is, the returned index is for the \e same Monomer object (pointer-wise). If the Monomer was found, the returned value is certain to be correct and \a ok is set to true. If the Monomer was not found, the returned value is 0 and ok is set to false. */ std::size_t Sequence::monomerIndex(MonomerCstSPtr monomer_csp, bool &ok) const { std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [monomer_csp](const MonomerSPtr &iter_monomer_sp) { return iter_monomer_sp == monomer_csp; }); if(the_iterator_cst != m_monomers.cend()) { ok = true; return std::distance(m_monomers.cbegin(), the_iterator_cst); } ok = false; return 0; } /*! \brief Returns the index of \a monomer_crp in this Sequence's list of Monomer instances. The search is based on comparison of the pointers, that is, the returned index is for the \e same Monomer object (pointer-wise). If the Monomer was found, the returned value is certain to be correct and \a ok is set to true. If the Monomer was not found, the returned value is 0 and ok is set to false. */ std::size_t Sequence::monomerIndex(MonomerCstRPtr monomer_crp, bool &ok) const { if(monomer_crp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [monomer_crp](const MonomerSPtr &iter_monomer_sp) { return iter_monomer_sp.get() == monomer_crp; }); if(the_iterator_cst != m_monomers.cend()) { ok = true; return std::distance(m_monomers.cbegin(), the_iterator_cst); } ok = false; return 0; } /*! \brief Seeks the next code occurring in the \a sequence string of Monomer codes. This function starts looking in \a sequence at \a index. The next found Monomer code is stored in \a code. If \a sequence is not a monomer code, it is set to \a err. Returns the count of characters that make \a code. This count can be used to search for the next code by setting its value incremented by 1 to \a index for a next function call. */ std::size_t Sequence::nextCode(const QString &sequence, QString &code, std::size_t &index, QString &err) { if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || !mcsp_polChemDef->isValid()) qFatalStream() << "Programming error. It is not possible that the PolChemDef be " "undefined or invalid."; QString new_code; std::size_t iter = 0; // We get a sequence of monomer codes(like "LysArgGlu" for example) // and we have to return the next code starting from *index. Note // that the sequence must not contain invalid characters. The // invalid characters might be placed in err for further scrutiny by // the caller. // Returns the count of actually parsed characters in the string // newCode(copied to 'code' param). If an error occurs -1 is // returned and the faulty character is copied in 'err'. 'index' is // updated with the index of the last valid character parsed for // current code. code.clear(); err.clear(); std::size_t sequence_length = sequence.length(); while(1) { if(iter >= static_cast(mcsp_polChemDef->getCodeLength())) { // Because we have progressed farther than authorized by // the number of characters allowed in the monomer codes // of this polymer chemistry definition, we decrement iter // and break the loop... Later in this function, we'll set // the proper index in the sequence where next parsing run // should occurs (the calling function will increment // index by one). --iter; break; } if(iter + index >= sequence_length) break; QChar curChar = sequence.at(iter + index); if(!curChar.isLetter()) { // qDebug() << __FILE__ << __LINE__ // << "The character is not a letter:" // << curChar; err = curChar; // The non-Letter character might be '/', which would be // perfectly fine, as we use it to symbolize the actual // cleavage site. Which means that we will continue // parsing the rest of the string : we have to give the // current position back to the caller in the index // variable for the next call to this function to start at // next character (not falling back to '/', which would // make us enter in an infinite loop). index = index + iter; return -1; } bool isLower = (curChar.category() == QChar::Letter_Lowercase); if(iter == 0) { if(isLower) { // qDebug() << __FILE__ << __LINE__ // << "First character of monomer code might not be" // << "lower case; sequence is" // << m_monomerText; err = curChar; return -1; } else { // Good, first char is uppercase. new_code += curChar; } } else //(iter != 0) { // We are not in our first iteration. So either the current // character is lowercase and we are just continuing to // iterate into a multi-char monomer code, or the current // character is uppercase, in which case we are starting to // iterate in a new monomer code. if(isLower) new_code += curChar; else { // Decrement iter, because this round was for nothing: // we had "invaded" the next monomer code in sequence, // which we must not do. --iter; break; } } ++iter; } // We finished parsing at most codeLength characters out of // sequence, so we have a valid code in the 'code' variable. We // can also compute a new index position in the sequence and return // the number of characters that we effectively parsed. Note that // the caller will be responsible for incrementing the 'index' value // by one character unit so as not to reparse the last characters of // the sent 'code' object. index = index + iter; code = new_code; err.clear(); return code.length(); } //////////////// MONOMER HANDLING FUNCTIONS ///////////////////// /*! \brief Inserts \a monomer at index \a index. If the index value is equal to the size of the Monomer container, then, the \a monomer is added to the bottom of the container. If the index value is greater than the size of the Monomer container, that is fatal. Returns the Uuid string corresponding to the new Monomer. */ QString Sequence::insertMonomerAt(const Monomer &monomer, std::size_t index) { if(index > size()) qFatalStream() << "Programming error. Index is out of bounds:" << index; else if(index == size()) return storeMonomer(std::make_shared(monomer)); else return storeMonomer(std::make_shared(monomer), index); // Should never reach this point. return QString(); } /*! \brief Removes the Monomer instance at index \a index from this Sequence's list of Monomer instances. An index that is out of bounds is fatal. Returns true. */ bool Sequence::removeMonomerAt(std::size_t index) { // qDebug() << "Asking to remove Monomer at index" << index; if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; // Some controls are in order: we have to check that at index, there is // a MonomerSPtr that is present both in m_monomers and in // m_uuidMonomerPairs. MonomerSPtr monomer_sp = m_monomers.at(index); qDebug() << "The Monomer being removed at index:" << index << "is:" << monomer_sp->getName() << "with modification status:" << monomer_sp->isModified(); QString uuid = getUuidForMonomer(monomer_sp); if(uuid.isEmpty()) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [uuid](const UuidMonomerWPtrPair &the_pair) { // Do not query the monomer_sp managed object because it can // be nullptr! return the_pair.first == uuid; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; m_uuidMonomerPairs.erase(the_iterator_cst); m_monomers.erase(m_monomers.begin() + index); // qDebug() << "Done removing Monomer."; return true; } /*! \brief Modifies the Monomer instance at index \a index with a Modif instance that is created on the basis of \a modif_name. If \a override is set to true, then the modification occurs even if the Monomer at \index was already modified max count times. An index that is out of bounds is fatal. Returns true. \sa Modif::m_maxCount */ bool Sequence::modifyMonomer(std::size_t index, const QString modif_name, bool override) { if(index >= size()) qFatalStream() << "Programming error. Index is out of bounds."; MonomerSPtr monomer_sp = getMonomerSPtrAt(index); ErrorList error_list; QString uuid = monomer_sp->modify(modif_name, override, &error_list); if(uuid.isEmpty() || error_list.size()) { qCritical() << "Monomer modification with Modif:" << modif_name << "failed"; return false; } return true; } /*! \brief Returns true if this Sequence instance, between Monomer indices \a left_index and \a right_index, has at least one modified Monomer instance. If no Monomer is modified, returns false. */ bool Sequence::hasModifiedMonomer(std::size_t left_index, std::size_t right_index) const { if(left_index >= size() || right_index >= size()) qFatal("Programming error. Indices out of bounds."); if(left_index > right_index) qFatal("Programming error. Indices are not correct."); for(std::size_t iter = left_index; iter <= right_index; ++iter) { if(m_monomers.at(iter)->isModified()) return true; } return false; } /*! \brief Returns a container holding all the indices corresponding to modified Monomer instances in the member Monomer container. The search is performed only between Monomer indices \a left_index and \a right_index. If no Monomer is modified, returns an empty container. */ std::vector Sequence::modifiedMonomerIndices(std::size_t left_index, std::size_t right_index) const { if(left_index >= size() || right_index >= size()) qFatal("Programming error. Indices out of bounds."); if(left_index > right_index) qFatal("Programming error. Indices are not correct."); std::vector indices; for(std::size_t iter = left_index; iter <= right_index; ++iter) { if(m_monomers.at(iter)->isModified()) indices.push_back(iter); } return indices; } //////////////// SEQUENCE SEARCH FUNCTIONS ///////////////////// /*! \brief Searches for a Sequence textual \a sequence_motif in this Sequence's container of Monomer instances, starting at \a index. \a sequence_motif, a text string is first converted to a container of Monomer instances (using the reference list of Monomer instances in the member polymer chemistry definition). Then, this Sequence's container of Monomer instances is searched for a Monomer stretch that matches that created for \a sequence_motif. As soon as a Monomer code stretch is found, the index in this Sequence's container of Monomer instances is set to \a index. Returns -1 if an error occurred, 1 if \a sequence_motif was found in this Sequence, 0 otherwise. */ int Sequence::findForwardMotif(const Sequence &sequence_motif, std::size_t &index) const { // qDebug() << "This sequence:" << getSequence(); // qDebug() << "The motif sequence:" << sequence_motif.getSequence(); if(!m_isValid) { qCritical() << "The Sequence is not valid."; return -1; } if(!size()) { qCritical() << "The Sequence is empty."; return 0; } // qDebug() << "motif:" << *(sequence_motif.monomerText()) << "index :" << // index; if(!sequence_motif.isValid()) { qCritical() << "The sequence motif to search for is not valid."; return -1; } if(index >= size()) return -1; int sequence_motif_size = sequence_motif.size(); // qDebug() << "The sequence motif has size:" << sequence_motif_size; // If motif's length is 0, then nothing to search for, return // unmodified 'index'. if(!sequence_motif_size) return 0; // Simple optimization, if index + size of motif is greater then // size of sequence, return right away. if(index + sequence_motif_size > size()) return 0; // Compare *this sequence with the one in 'motif', starting at index // 'index' in *this sequence and 0 in 'motif'. bool matched = false; int matchIndex = 0; for(std::size_t iter = index; iter < size(); ++iter) { std::size_t jter = 0; const MonomerSPtr monomer_sp = getMonomerCstSPtrAt(iter); const MonomerSPtr motif_monomer_sp = sequence_motif.getMonomerCstSPtrAt(jter); // qDebug() << "At sequence iter" << iter << monomer_p->getCode() // << "and at motif jter:" << jter << motif_monomer_p->getCode(); // We do not compare with operator == because that comparison // would involve the comparison of modifications inside the // monomers, which would not work here. if(monomer_sp->getCode() != motif_monomer_sp->getCode()) continue; // An easy check is to see if the number of remaining monomers // in the polymer sequence is compatible with the number of // monomers still to be matched in the find array. Imagine the // sequence of the polymer ends like this: ==========JTOUTVU and // the sequence to be searched for is : TVUL What we see is that // the T of the TVU of the sequence matches; however we can stop // the search right away because there is a 'L' in the search // pattern that is not present in the end part of the // sequence. This is exactly what is checked below. Note that // this check makes SURE that at the end of the second inner // loop, when we get out of it, the sole reason we may not // consider that the match did not occur is because actually two // monomers differred and not because anybody came out of the // borders of the sequence in neither the array of the sequence // to be searched, nor the array of the polymer sequence. This // makes it very easy to assess if a match occurred or not. if(size() - iter < sequence_motif.size() - jter) { // Note that if it were ==, then it would have been possible // that the sequence "just-in-time" match prior to ending of // the polymer sequence array. Do not forget that we are in // forward mode, thus we can break immediately, because we // are certain that we won't have any chance to find the // sequence downstream of current index. matched = false; break; } // qDebug() << monomer_p->getCode() // << "found at sequence iter index: " << iter; matchIndex = iter; // We have to set the matched boolean to true, because if the // motif to find is one monomer-long, then the loop below will // not be entered, and we'll fail to know that the match // occurred later on. matched = true; // Now that we have our anchoring point in the *this sequence, // let's iterate in the motif, and check if the identity in // sequence goes along. for(std::size_t kter = jter + 1; kter < sequence_motif.size(); ++kter) { // At first run in this loop, we are in the second cell of // the find list, which means that we should have jter == // 1. And we should compare its contents with those of the // cell in the sequence list at index(iter + jter). const MonomerSPtr monomer_sp = getMonomerCstSPtrAt(iter + kter); const MonomerSPtr motif_monomer_sp = sequence_motif.getMonomerCstSPtrAt(kter); // qDebug() << "At sequence iter + kter" << iter + kter // << monomer_p->getCode() << "and at motif kter:" << kter // << motif_monomer_p->getCode(); // We compare codes and not monomers because that // comparison would involve the comparison of modifications // inside the monomers, which would not work here. if(monomer_sp->getCode() == motif_monomer_sp->getCode()) { // The monomers still match. matched = true; // qDebug() << "still matching"; continue; } else { matched = false; // qDebug() << "not matching anymore"; break; } } // End of // for (int kter = jter + 1 ; kter < motif->size() ; ++kter) // At this point, we either have normally extinguished the run // in the inner loop, or we have gone out of it before its // normal termination. In either case, we have to test if the // match occurred or not. // Check if the match did NOT occur: if(!matched) { // We just continue with the outer loop, that is, we continue // searching in this sequence for a match with the // first monomer in the motif. continue; } else { // The match indeed occurred. index = matchIndex; return 1; } } // End of // for (int iter = index; iter < size(); ++iter) // No match could be achieved, we have to let the caller function // know this in a durable manner : returning 0. return 0; } //////////////// DIAGNOSTICS FUNCTIONS ///////////////////// /*! \brief Returns the size of this Sequence as the size of the container of Monomer instances. */ std::size_t Sequence::size() const { return m_monomers.size(); } /*! \brief Returns true if \a index is valid as an index of a Monomer instance in this Sequence's container of \l{Monomer} instances, false otherwise. */ bool Sequence::isInBound(std::size_t index) { if(index < size()) return true; return false; } //////////////// OPERATORS ///////////////////// /*! \brief Assigns \a other to this Sequence. The copying is deep with the instances in the \a other container of Monomer instances being reinstantiated anew into this Sequence's container of Monomer instances. Returns a reference to this Sequence. */ Sequence & Sequence::operator=(const Sequence &other) { if(&other == this) return *this; mcsp_polChemDef = other.mcsp_polChemDef; m_monomers.clear(); for(const MonomerSPtr &monomer_sp : other.m_monomers) storeMonomer(monomer_sp); return *this; } /*! \brief Returns true if the \c this Sequence is identical to \a other, false otherwise. The comparison of the Monomer instances is deep and is not based on merely comparing the pointers. */ bool Sequence::operator==(const Sequence &other) { if(&other == this) return true; if(mcsp_polChemDef != other.mcsp_polChemDef) { qInfo() << "Differing PolChemDef."; return false; } if(m_monomers.size() != other.m_monomers.size()) { qInfo() << "Differing Monomer container size."; return false; } for(std::size_t iter = 0; iter < other.m_monomers.size(); ++iter) { if(*m_monomers.at(iter) != *other.m_monomers.at(iter)) return false; } return true; } /*! \brief Returns true if the \c this Sequence is different than \a other, false otherwise. Returns the negation of operator==(other). */ bool Sequence::operator!=(const Sequence &other) { if(&other == this) return false; return !operator==(other); } //////////////// VALIDATIONS ///////////////////// /*! \brief Validates this Sequence using the member polymer chemistry definition as the reference polymer chemistry definition and sets m_isValid to the result of this validation. Returns true if all the Monomer instances in the member container could be found in the polymer chemistry definition's container of reference Monomer instances. Any error is documented by storing a message to \a error_list_p. \sa makeMonomers() */ bool Sequence::validate(ErrorList *error_list_p) const { qsizetype error_count = error_list_p->size(); if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr || !mcsp_polChemDef->isValid()) { qCritical() << "The PolChemDef is not available."; error_list_p->push_back( "The PolChemDef is not available, cannot validate the Sequence " "instance."); } for(const MonomerSPtr &monomer_sp : m_monomers) { if(monomer_sp->isKnownByCodeInPolChemDef() != Enums::PolChemDefEntityStatus::ENTITY_KNOWN) { qCritical() << "At least one Monomer was not found in the PolChemDef."; error_list_p->push_back( "At least one Monomer was not found in the PolChemDef"); } } m_isValid = (error_list_p->size() > error_count ? false : true); return m_isValid; } /*! \brief Returns the validity status of this Sequence instance. */ bool Sequence::isValid() const { return m_isValid; } //////////////// UTILS ///////////////////// /*! \brief Removes all spaces, tabulations, carriage returns and linefeeds from the \a monomer_text sequence of Monomer codes. */ QString Sequence::unspacifySequence(const QString &monomer_text) { // Removal of all spaces, carriage returns and linefeeds: QString local_monomer_text = monomer_text; for(int iter = local_monomer_text.length() - 1; iter >= 0; --iter) { QChar curChar = local_monomer_text.at(iter); if(curChar == QChar::Tabulation || curChar == QChar::LineFeed || curChar == QChar::FormFeed || curChar == QChar::CarriageReturn || curChar == QChar::Space || curChar == QChar::Nbsp || curChar == QChar::SoftHyphen) local_monomer_text.remove(iter, 1); } return local_monomer_text; } /*! \brief Stores the Monomer instance pointer \a monomer_sp in the member container and returns the Uuid string associated to it. */ QString Sequence::storeMonomer(const MonomerSPtr &monomer_sp) { if(monomer_sp == nullptr) qFatalStream() << "The provided MonomerSPtr is nullptr."; // qDebug() << "Right before storage, there are currently" << // m_monomers.size() // << "monomers."; // Do not store an item twice. if(hasMonomer(monomer_sp) || !getUuidForMonomer(monomer_sp).isEmpty()) qFatalStream() << "It is prohibited to store the same MonomerCstSPtr more than once."; // Even if we get a ref to shared_ptr, the reference count increment will // occur. m_monomers.push_back(monomer_sp); QString uuid = QUuid::createUuid().toString(); m_uuidMonomerPairs.push_back(UuidMonomerWPtrPair(uuid, monomer_sp)); // qDebug() << "Right after storage, there are currently" << m_monomers.size() // << "monomers."; return uuid; } /*! \brief Stores the Monomer instance pointer in the member container and returns the Uuid string associated to it. */ QString Sequence::storeMonomer(const MonomerSPtr &monomer_sp, std::size_t index) { if(monomer_sp == nullptr) qFatalStream() << "The provided MonomerSPtr is nullptr."; // qDebug() << "Right before storage, there are currently" << // m_monomers.size() // << "monomers."; // Do not store an item twice. if(hasMonomer(monomer_sp) || !getUuidForMonomer(monomer_sp).isEmpty()) qFatalStream() << "It is prohibited to store the same MonomerCstSPtr more than once."; // Even if we get a ref to shared_ptr, the reference count increment will // occur. m_monomers.emplace(m_monomers.begin() + index, monomer_sp); QString uuid = QUuid::createUuid().toString(); m_uuidMonomerPairs.emplace(m_uuidMonomerPairs.begin() + index, UuidMonomerWPtrPair(uuid, monomer_sp)); // qDebug() << "Right after storage, there are currently" << m_monomers.size() // << "monomers."; return uuid; } /*! \brief Returns true if \a monomer_sp was found in the member container of Monomer instances, false otherwise. */ bool Sequence::hasMonomer(const MonomerSPtr &monomer_sp) const { if(monomer_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_monomers.cbegin(), m_monomers.cend(), [monomer_sp](const MonomerSPtr &the_monomer_csp) { return the_monomer_csp == monomer_sp; }); if(the_iterator_cst == m_monomers.cend()) return false; // No sanity checks with getMonomerFromUuid() or getUuidForMonomer() // because that makes circular calls (these functions make sanity // checks by calling this hasMonomer().) return true; } /*! \brief Returns true if \a monomer_sp was found in the member container of Uuid-Monomer pairs, false otherwise. */ bool Sequence::hasUuid(const MonomerSPtr &monomer_sp) const { if(monomer_sp == nullptr) qFatalStream() << "Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [monomer_sp](const UuidMonomerWPtrPair &the_pair) { return the_pair.second.lock() == monomer_sp; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) return false; // Sanity check if(!hasMonomer(monomer_sp)) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; return true; } /*! \brief Returns the Monomer instance pointer in the member container that is associated to the \a uuid Uuid string. If no such Monomer instance pointer is found, nullptr is returned. */ MonomerSPtr Sequence::getMonomerForUuid(const QString &uuid) const { // qDebug() << "There are currently" << m_monomers.size() // << "monomers. The uuid that is asked for:" << uuid; std::vector>::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [uuid](const UuidMonomerWPtrPair &the_pair) { return the_pair.first == uuid; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) return nullptr; MonomerSPtr monomer_csp = (*the_iterator_cst).second.lock(); // Sanity check if(!hasMonomer(monomer_csp)) qFatalStream() << "Inconsistency between m_monomers and m_uuidMonomerPairs."; // qDebug() << "Found Monomer:" << monomer_csp->getCode() // << "with modification status:" << monomer_csp->isModified(); return monomer_csp; } /*! \brief Returns the UUID string identifying \a monomer_sp in the member container. If no such Monomer is found, an empty string is returned. */ QString Sequence::getUuidForMonomer(const MonomerSPtr &monomer_sp) const { if(monomer_sp == nullptr) qFatalStream() << "Programming error. Pointer cannot be nullptr."; std::vector::const_iterator the_iterator_cst = std::find_if(m_uuidMonomerPairs.cbegin(), m_uuidMonomerPairs.cend(), [monomer_sp](const UuidMonomerWPtrPair &the_pair) { // Do not query the monomer_sp managed object because it can // be nullptr! return the_pair.second.lock() == monomer_sp; }); if(the_iterator_cst == m_uuidMonomerPairs.cend()) { // Sanity check if(hasMonomer(monomer_sp)) qFatalStream() << "Inconsistency between the m_monomers and the " "m_uuidMonomerPairs vectors."; return QString(); } // Sanity check if(!hasMonomer(monomer_sp)) qFatalStream() << "Inconsistency between the m_monomers and the " "m_uuidMonomerPairs vectors."; return (*the_iterator_cst).first; } /*! \brief Returns a container of QString instances that correspond to the UUID strings that identify all the Monomer instances involved in this Sequence. If no Monomer is found, an empty container is returned. */ std::vector Sequence::getAllMonomerUuids() const { std::vector the_uuid_strings; for(const UuidMonomerWPtrPair &pair : m_uuidMonomerPairs) the_uuid_strings.push_back(pair.first); // Sanity check if(the_uuid_strings.size() != m_monomers.size()) qFatalStream() << "Inconsistency between the _s and pairs."; return the_uuid_strings; } /*! \brief Removes from the member container all the Monomer instance pointers that are not found to still be alive. */ void Sequence::cleanupMonomers() { // qDebug() << "At beginning, count of UUID-Monomer pairs:" // << m_uuidMonomerPairs.size(); std::vector::iterator the_iterator = m_uuidMonomerPairs.begin(); std::vector::iterator the_end_iterator = m_uuidMonomerPairs.end(); while(the_iterator != the_end_iterator) { if((*the_iterator).second.expired() || (*the_iterator).second.lock() == nullptr || !hasMonomer((*the_iterator).second.lock())) the_iterator = m_uuidMonomerPairs.erase(the_iterator); else ++the_iterator; } // qDebug() << "At end, count of UUID-Monomer pairs:" // << m_uuidMonomerPairs.size(); } /*! \brief Returns a checksum calculated on this Sequence's portion contained in [\a index_start -- \a index_stop]. The sequence matching the [\a index_start -- \a index_stop] range is extracted from m_monomerText, with (\a with_modifs is true) or without (\a with_modifs is false) the monomer modifications. The checksum is computed on that extracted string. Returns the checksum. */ quint16 Sequence::checksum(int index_start, int index_stop, bool with_modifs) const { // qDebug() << "index_start:" << index_start << "index_stop" << index_stop; if(!size()) return 0; if(index_start > index_stop) std::swap(index_start, index_stop); QString text = getSequence(index_start, index_stop, with_modifs); QByteArray bytes = text.toUtf8(); quint16 checksum = qChecksum(QByteArrayView(bytes)); // qDebug() << __FILE__ << __LINE__ // << "checksum:" << checksum; return checksum; } /*! \brief Reset this Sequence instance to default values. */ void Sequence::clear() { m_monomers.clear(); m_uuidMonomerPairs.clear(); m_isValid = false; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Tolerance.cpp000664 001750 001750 00000011743 15100504560 024711 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// stdlib includes /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassGui includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Tolerance.hpp" namespace MsXpS { namespace libXpertMassCore { Tolerance::Tolerance(QObject *parent) : QObject(parent) { } Tolerance::Tolerance(Tolerance::Type type, double nominal, QObject *parent) : QObject(parent), m_nominal(nominal), m_type(type) { } void Tolerance::setNominal(double nominal) { if(m_nominal == nominal) return; m_nominal = nominal; emit nominalChangedSignal(); emit toleranceChangedSignal( std::pair(m_nominal, m_type)); } double Tolerance::getNominal() const { return m_nominal; } void Tolerance::setType(Type type) { if(m_type == type) return; m_type = type; emit typeChangedSignal(); emit toleranceChangedSignal( std::pair(m_nominal, m_type)); } void Tolerance::setType(const QString &text) { std::map::const_iterator the_iterator_cst = std::find_if(m_typeToStringMap.cbegin(), m_typeToStringMap.cend(), [&](const std::pair &pair) { if(pair.second == text) return true; return false; }); if(the_iterator_cst != m_typeToStringMap.cend()) m_type = (*the_iterator_cst).first; else qFatalStream() << "Programming error. Tolerance type as string is not known."; emit typeChangedSignal(); emit toleranceChangedSignal( std::pair(m_nominal, m_type)); } Tolerance::Type Tolerance::getType() const { return m_type; } Tolerance::Type Tolerance::getType(const QString &text) const { std::map::const_iterator the_iterator_cst = std::find_if(m_typeToStringMap.cbegin(), m_typeToStringMap.cend(), [&](const std::pair &pair) { if(pair.second == text) return true; return false; }); if(the_iterator_cst != m_typeToStringMap.cend()) return (*the_iterator_cst).first; return Tolerance::Type::NOT_SET; } QString Tolerance::getTypeAsString() const { std::map::const_iterator the_iterator_cst = std::find_if(m_typeToStringMap.cbegin(), m_typeToStringMap.cend(), [&](const std::pair &pair) { if(pair.first == m_type) return true; return false; }); if(the_iterator_cst != m_typeToStringMap.cend()) return (*the_iterator_cst).second; return QString(); } void Tolerance::initialize(double nominal, Type type) { m_nominal = nominal; m_type = type; emit toleranceChangedSignal( std::pair(m_nominal, m_type)); } void Tolerance::initialize(const Tolerance &tolerance) { initialize(tolerance.m_nominal, tolerance.m_type); } Tolerance * Tolerance::clone(QObject *parent) { Tolerance *copy = new Tolerance(m_type, m_nominal, parent); return copy; } double Tolerance::calculateWidth(double reason) const { double width = 0; if(m_type == Type::DALTON) width = m_nominal; else if(m_type == Type::RES) width = reason / m_nominal; else if(m_type == Type::PPM) width = reason / 1000000 * m_nominal; else qFatalStream() << "Programming error. The Type is not valid."; emit widthChangedSignal(width); return width; } QString Tolerance::toString() const { QString type_as_text = getTypeAsString(); return QString("%1 %2").arg(m_nominal).arg(type_as_text); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/Utils.cpp000664 001750 001750 00000054546 15100504560 024105 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2024 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ ////////////////////////////// Stdlib includes #include #include ////////////////////////////// Qt includes #include #include #include #include ////////////////////////////// Local includes #include "MsXpS/libXpertMassCore/Utils.hpp" namespace MsXpS { namespace libXpertMassCore { /*! \class MsXpS::libXpertMassCore::Utils \inmodule libXpertMassCore \ingroup XpertMassCoreUtilities \inheaderfile Utils.hpp \brief The Utils class provides a number of utilitary features that may be of use anywhere in the XpertMass source code tree. */ /*! \variable MsXpS::libXpertMassCore::Utils::subFormulaRegExp \brief Regular expression used to deconstruct the main actionformula into minus and plus component subformulas. This regular expression does not account for the \"\" title of the actionformula. \code "([+-]?)([A-Z][a-z]*)(\\d*[\\.]?\\d*)" \endcode \sa Formula::splitActionParts() */ QRegularExpression Utils::subFormulaRegExp = QRegularExpression(QString("([+-]?)([A-Z][a-z]*)(\\d*[\\.]?\\d*)")); /*! \variable MsXpS::libXpertMassCore::Utils::xyFormatMassDataRegExp \brief Regular expression that matches the m/z,i pairs in text files. */ QRegularExpression Utils::xyFormatMassDataRegExp = QRegularExpression("^(\\d*\\.?\\d+)([^\\d^\\.^-]+)(-?\\d*\\.?\\d*[e-]?\\d*)"); /*! \variable MsXpS::libXpertMassCore::Utils::endOfLineRegExp \brief Regular expression that matches the end of line in text files. */ QRegularExpression Utils::endOfLineRegExp = QRegularExpression("^\\s+$"); /*! \variable MsXpS::libXpertMassCore::Utils::xmlIndentationToken \brief String used to craft the indentation of the XML elements. */ QString Utils::xmlIndentationToken = QString(" "); /*! \brief Constructs a Utils instance setting parent to \a parent. */ Utils::Utils(QObject *parent): QObject(parent) { } /*! \brief Destructs a Utils instance. */ Utils::~Utils() { } /*! \brief Configures the format of all the messages that are output using qInfo(), qWarning(), qCritical(), qFatalStream() and qDebug() using a number of parameters. \list \li \a type The kind of message \li \a context The context of the message \li \a msg The message to output. \endlist */ void Utils::messageOutputFormat(QtMsgType type, const QMessageLogContext &context, const QString &msg) { // Define ANSI color codes #define RESET "\033[0m" #define BLACK "\033[30m" #define RED "\033[31m" #define GREEN "\033[32m" #define YELLOW "\033[33m" #define BLUE "\033[34m" #define MAGENTA "\033[35m" #define CYAN "\033[36m" #define WHITE "\033[36m" #define BOLD "\033[1m" QByteArray localMsg = msg.toLocal8Bit(); const char *file = context.file ? context.file : ""; const char *function = context.function ? context.function : ""; QString prefix; QString color; switch(type) { case QtInfoMsg: prefix = "INFO: "; color = QString("%1").arg(GREEN); break; case QtWarningMsg: prefix = "WARNING: "; color = QString("%1").arg(BLUE); break; case QtCriticalMsg: prefix = "CRITICAL: "; color = QString("%1").arg(MAGENTA); break; case QtFatalMsg: prefix = "FATAL: "; color = QString("%1").arg(RED); break; case QtDebugMsg: prefix = "DEBUG: "; color = QString("%1").arg(YELLOW); break; } fprintf(stderr, "%s%s%s%s:%s%u%s\n%s\n%s======> %s%s\n\n", color.toLocal8Bit().constData(), prefix.toLocal8Bit().constData(), RESET, file, color.toLocal8Bit().constData(), context.line, RESET, function, color.toLocal8Bit().constData(), localMsg.constData(), RESET); } /*! \brief Installs the message handler. \sa Utils::messageOutputFormat() */ void Utils::configureDebugMessagesFormat() { qInstallMessageHandler(messageOutputFormat); } /*! \brief Returns the average of the \a list of \c double values. All the values in list are process in order to compute the following: \list \li \a sum \li \a average \li \a variance \li \a std_dev \li \a smallest_non_zero \li \a smallest \li \a smallest_median \li \a greatest \endlist statistical values if the corresponding parameters are non-nullptr. \sa doubleVectorStatistics() */ void Utils::doubleListStatistics(QList list, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &smallest_median, double &greatest) { // Sort the list, we'll need it sorted to compute the median value. std::sort(list.begin(), list.end()); int count = list.size(); if(count == 0) return; // Sorted list, the smallest is the first. smallest = list.first(); // The greatest is the last. greatest = list.last(); sum = 0; smallest_non_zero = std::numeric_limits::max(); for(int iter = 0; iter < count; ++iter) { double current = list.at(iter); sum += current; // If current is non-zero, then take its value into account. if(current > 0 && current < smallest_non_zero) smallest_non_zero = current; } average = sum / count; double varN = 0; for(int iter = 0; iter < count; ++iter) { varN += (list.at(iter) - average) * (list.at(iter) - average); } variance = varN / count; std_dev = std::sqrt(variance); // Now the median value // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "count:" << count; if(count == 1) { smallest_median = list.first(); } else { if(count % 2 == 0) smallest_median = (list.at(count / 2 - 1) + list.at(count / 2)) / 2; else smallest_median = list.at(count / 2 + 1); } } /*! \brief Returns the average of the \a vector of \c double values. All the values in the container are processed in order to compute the following: \list \li \a sum \li \a average \li \a variance \li \a std_dev \li \a smallest_non_zero \li \a smallest \li \a smallest_median \li \a greatest \endlist statistical values if the corresponding parameters are non-nullptr. \sa doubleListStatistics() */ void Utils::doubleVectorStatistics(std::vector &vector, double &sum, double &average, double &variance, double &std_dev, double &smallest_non_zero, double &smallest, double &smallest_median, double &greatest) { // Sort the vector, we'll need it sorted to compute the median value. std::sort(vector.begin(), vector.end()); int count = vector.size(); if(!count) return; // Sorted vector, the smallest is the first. smallest = vector.front(); // The greatest is the last. greatest = vector.back(); sum = 0; smallest_non_zero = std::numeric_limits::max(); for(double &value : vector) { double current = value; sum += current; // If current is non-zero, then take its value into account. if(current > 0 && current < smallest_non_zero) smallest_non_zero = current; } average = sum / count; double varN = 0; for(auto &value : vector) { varN += (value - average) * (value - average); } variance = varN / count; std_dev = std::sqrt(variance); // Now the median value // qDebug() << "count:" << count; if(count == 1) { smallest_median = vector.front(); } else { if(count % 2 == 0) smallest_median = (vector.at(count / 2 - 1) + vector.at(count / 2)) / 2; else smallest_median = vector.at(count / 2 + 1); } } /*! \brief Returns true if both double values \a value1 and \a value2, are equal within the double representation capabilities of the platform, false otherwise. In the comparison, the \a decimal_places are taken into account. */ bool Utils::almostEqual(double value1, double value2, int decimal_places) { // QString value1String = QString("%1").arg(value1, // 0, 'f', 60); // QString value2String = QString("%1").arg(value2, // 0, 'f', 60); // qWarning() << __FILE__ << __LINE__ << __FUNCTION__ //<< "value1:" << value1String << "value2:" << value2String; // The machine epsilon has to be scaled to the magnitude of the values used // and multiplied by the desired precision in ULPs (units in the last place) // (decimal places). double sum_of_values = std::abs(value1 + value2); // QString sum_of_valuesString = QString("%1").arg(sum_of_values, // 0, 'f', 60); double diff_of_values = std::abs(value1 - value2); // QString diff_of_valuesString = QString("%1").arg(diff_of_values, // 0, 'f', 60); double epsilon = std::numeric_limits::epsilon(); // QString epsilonString = QString("%1").arg(epsilon, // 0, 'f', 60); double scale_factor = epsilon * sum_of_values * decimal_places; // QString scale_factorString = QString("%1").arg(scale_factor, // 0, 'f', 60); // qWarning() << "diff_of_values:" << diff_of_valuesString << "sum_of_values:" // << sum_of_valuesString << "epsilon:" << epsilonString << "scale_factor:" << // scale_factorString; bool res = diff_of_values < scale_factor // unless the result is subnormal: || diff_of_values < std::numeric_limits::min(); // qWarning() << __FILE__ << __LINE__ << __FUNCTION__ //<< "returning res:" << res; return res; } /*! \brief Provides a text stream handle to the standard output. Use: \code qStdOut() << __FILE__ << __LINE__ << "text to the standard output," << "not the error standard output." \endcode Returns a reference to a static QTextStream that directs to the standard output. */ QTextStream & Utils::qStdOut() { static QTextStream ts(stdout); return ts; } /*! \brief Removes all space characters from a copy of the \a text string and returns the new string. */ QString Utils::unspacify(const QString &text) { if(text.isEmpty()) { return text; } QString unspacified = text; unspacified.remove(QRegularExpression("\\s+")); return unspacified; } /*! \brief Returns a string with a binary representation of the \a value integer. */ QString Utils::binaryRepresentation(int value) { QString string; string = QString("%1").arg(value, 32, 2); return string; } /*! \brief Returns a shortened (elided) version of the \a text string. It is sometimes necessary to display, in a graphical user interface a very long string, that cannot fit in the provided widget. This function returns a shortened version of the input string. For example, "Interesting bits of information are often lost where there are too many details", becomes "Interes...details". \a chars_left: Count of characters to be kept on the left side of the string. \a chars_right: Count of characters to be kept on the right side of the string. \a delimiter string to use as the elision delimiter (in the above example, that is '.'). */ QString Utils::elideText(const QString &text, int chars_left, int chars_right, const QString &delimiter) { // We want to elide text. For example, imagine we have text = "that // is beautiful stuff", with charsLeft 4 and charsRight 4 and // delimitor "...". Then the result would be "that...tuff" if(chars_left < 1 || chars_right < 1) { return text; } int size = text.size(); // If the text string is already too short, no need to elide // anything. if((chars_left + chars_right + delimiter.size()) >= size) { return text; } QString result = text.left(chars_left); result.append(delimiter); result.append(text.right(chars_right)); return result; } /*! \brief Returns a string containing a paragraph-based version of the very long single-line \a text. When a text string is too long to be displayed in a line of reasonable length, inserts newline characters at positions calculated to yield a paragraph of the given \a width. \sa stanzifyParagraphs() */ QString Utils::stanzify(const QString &text, int width) { QString result = text; // First, replace all the existing newline chars with spaces. result = result.replace("\n", " "); int iter = width; // Then, iterate in the obtained string and every width characters try to // insert a newline character by iterating back to the left and searching // for a space. for(; iter < result.size(); iter += width) { // Now iterate in reverse and search for a space where to insert a // newline int jter = iter; for(; jter >= 0; --jter) { if(result.at(jter) == ' ') { result[jter] = '\n'; break; } } } return result; } /*! \brief Returns a string containing a series of paragraph-based versions of the very long single-line-containing paragraphs in \a text. \a text is a string with newline characters that delimit paragraph that thelmselves are made of a very long single line. This function splits \a text along the \c '\n' character and each obtained very long single-line string is reduced to a set of lines to make a pararagraph (see \l{stanzify}). The with of the line in that generated paragraph is \a width. \sa stanzify() */ QString Utils::stanzifyParagraphs(const QString &text, int width) { QString result; QStringList paragraphList = text.split("\n"); for(int iter = 0; iter < paragraphList.size(); ++iter) { QString line = paragraphList.at(iter); QString stanzifiedLine = stanzify(line, width); result.append(stanzifiedLine); result.append("\n"); } return result; } /*! \brief Returns the number of zero decimals in \a value that are found between the decimal point and the first non-zero decimal. For example, 0.11 would return 0 (no empty decimal) 2.001 would return 2 1000.0001254 would return 3 */ int Utils::countZeroDecimals(double value) { int intPart = static_cast(value); double decimalPart = value - intPart; int count = -1; while(1) { ++count; decimalPart *= 10; if(decimalPart > 1) return count; } return count; } /*! \brief Returns the delta value corresponding to \a value and \a ppm part-per-million. */ double Utils::ppm(double value, double ppm) { return (value * ppm) / 1000000; } /*! \brief Returns \a value incremented by the matching \a ppm part. */ double Utils::addPpm(double value, double ppm) { return value + (value * ppm) / 1000000; } /*! \brief Returns \a value decremented by the matching \a ppm part. */ double Utils::removePpm(double value, double ppm) { return value - (value * ppm) / 1000000; } /*! \brief Return the delta corresponding to \a value and resolution \a res. */ double Utils::res(double value, double res) { return value / res; } /*! \brief Returns \a value incremented by the matching \a res part. */ double Utils::addRes(double value, double res) { return value + (value / res); } /*! \brief Returns \a value decremented by the matching \a res part. */ double Utils::removeRes(double value, double res) { return value - (value / res); } /*! \fn template QString Utils::pointerAsString(T *ptr) \brief Returns a string representing the \a ptr address location. \sa stringAsPointer */ template QString Utils::pointerAsString(T *ptr) { return QString::number(reinterpret_cast(ptr), 16); // Convert to hex string } /*! \fn template T * Utils::stringAsPointer(const QString &str) \brief Returns a pointer to type T corresponding the \a str representation of the pointer. \sa pointerAsString() */ template T * Utils::stringAsPointer(const QString &str) { bool ok; quintptr intPtr = str.toULongLong(&ok, 16); // Convert hex string back to integer return ok ? reinterpret_cast(intPtr) : nullptr; } /*! \brief Returns a string holding the the file path to the configuration settings for application \a module_name. */ QString Utils::craftConfigSettingsFilePath(const QString &module_name) { // The configuration settings file for all the settings of the program QString file_path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); if(file_path.isEmpty()) file_path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); file_path = file_path + QDir::separator() + module_name; file_path = file_path + QDir::separator() + "configSettings.ini"; return file_path; } /*! \brief Returns 0 if both files \a file_path_1 and \a file_path_2 have the same contents or 1 if theirs contents differ. Returns -1 if any file could not be opened. */ int Utils::testDiffBetweenTwoTextFiles(const QString &file_path_1, const QString &file_path_2) { QFile file_1(file_path_1); if(!file_1.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << file_path_1 << "could not be opened"; return -1; } QFile file_2(file_path_2); if(!file_2.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug() << file_path_2 << "could not be opened"; return -1; } QTextStream in_stream_1(&file_1), in_stream_2(&file_2); while(!in_stream_1.atEnd() && !in_stream_2.atEnd()) { QString line_1 = in_stream_1.readLine(); QString line_2 = in_stream_2.readLine(); if(line_1 != line_2) return 1; } return 0; } /*! \brief Returns a string obtained by concatenating all the strings in the \a error_list container. Each string is separated from the other with \a separator. */ QString Utils::joinErrorList(const ErrorList &error_list, const QString &separator) { QString text; for(const QString &string : error_list) text += QString("%1%2").arg(string).arg(separator); return text; } #ifdef Q_OS_WIN #include #include #include bool Utils::isProcessRunningInWindows(const QString &process_name) { bool isRunning = false; PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if(Process32First(snapshot, &entry)) { while(Process32Next(snapshot, &entry)) { if(QString::fromWCharArray(entry.szExeFile) == process_name) { isRunning = true; break; } } } CloseHandle(snapshot); return isRunning; } #endif #ifdef Q_OS_LINUX #include #include bool Utils::isProcessRunningInLinux(const QString &process_name) { QDir procDir("/proc"); QStringList processes = procDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for(const QString &pid : processes) { // Check if it's a valid PID (numeric) bool ok; pid.toInt(&ok); if(!ok) continue; QFile cmdlineFile(QString("/proc/%1/comm").arg(pid)); if(cmdlineFile.open(QIODevice::ReadOnly)) { QString name = QString::fromLocal8Bit(cmdlineFile.readAll()).trimmed(); if(name == process_name) { return true; } } } return false; } #endif bool Utils::isProgramRunning(const QString &program_name) { // Try the QProcess method first (most portable) QProcess process; #ifdef Q_OS_WIN process.start("tasklist", QStringList() << "/NH" << "/FI" << QString("IMAGENAME eq %1").arg(program_name)); #else process.start("pgrep", QStringList() << "-x" << program_name); #endif if(process.waitForFinished(1000)) { QString output = process.readAllStandardOutput(); #ifdef Q_OS_WIN return output.contains(program_name, Qt::CaseInsensitive); #else return !output.trimmed().isEmpty(); #endif } // Fallback to platform-specific methods if QProcess fails #ifdef Q_OS_WIN return isProcessRunningInWindows(program_name); #elif defined(Q_OS_LINUX) return isProcessRunningInLinux(program_name); #else return false; // macOS or other platforms #endif } void Utils::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register Utils class: engine is null"; return; } // Register the meta object as a constructor qDebug() << "Registering JS constructor for class Utils."; // The registration below makes it possible to allocate a Utils // instance using var the_utils = new Utils() // Then the functions are used as the_utils.unspacify("This text has spaces") QJSValue jsMetaObject = engine->newQMetaObject(&Utils::staticMetaObject); engine->globalObject().setProperty("Utils", jsMetaObject); // The registration below creates a singleton exposed as "utils" which // makes it possible to use the static functions as // utils.unspacify("This text has spaces") without first allocating // an instance of the class. engine->globalObject().setProperty("utils", engine->newQObject(new Utils(engine))); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/XpertMassCoreJavaScript.cpp000664 001750 001750 00000012717 15100504560 027525 0ustar00rusconirusconi000000 000000 #include "MsXpS/libXpertMassCore/XpertMassCoreJavaScript.hpp" namespace MsXpS { namespace libXpertMassCore { // This function needs to be called this way: // MsXpS::libXpertMassCore::registerEnumsToQJSEngine(engine_p) // in the program or library linking to this library. void registerEnumsToQJSEngine(QJSEngine *engine) { // qDebug() << "Now registering the Enums:: enums for MsXpS::libXpertMassCore"; // Get the meta-object for the namespace (generated by Q_NAMESPACE) const QMetaObject *metaObject = &Enums::staticMetaObject; if(metaObject == nullptr) qFatal() << "Programming error. Failed to obtain &Enums::staticMetaObject"; // qDebug() << "Found namespace Enums with full name:" // << metaObject->className(); QJSValue libraryJsRootProperty; if(engine->globalObject().hasProperty("libXpertMassCore")) { // qDebug() << "Global object property 'libXpertMassCore' already exists."; libraryJsRootProperty = engine->globalObject().property("libXpertMassCore"); } else { // qDebug() << "Global object property 'libXpertMassCore' not found."; // Create a JS object for libXpertMassCore libraryJsRootProperty = engine->newObject(); } // Create a nested JS object for Enums QJSValue all_js_enums = engine->newObject(); // Loop through all enums and populate jsEnums for(int i = 0; i < metaObject->enumeratorCount(); ++i) { QMetaEnum iter_meta_enum = metaObject->enumerator(i); // qDebug() << "Now iterating in enum:" << iter_meta_enum.enumName(); QJSValue single_js_enum = engine->newObject(); for(int j = 0; j < iter_meta_enum.keyCount(); ++j) { // qDebug() << "Now iterating in new key:" << iter_meta_enum.key(j) // << "with value:" << iter_meta_enum.value(j); // key() would be "LEFT", with value() = 1 single_js_enum.setProperty(iter_meta_enum.key(j), iter_meta_enum.value(j)); } // name would be CapType all_js_enums.setProperty(iter_meta_enum.enumName(), single_js_enum); } // Attach Enums to libXpertMassCore libraryJsRootProperty.setProperty("Enums", all_js_enums); engine->globalObject().setProperty("libXpertMassCore", libraryJsRootProperty); #if 0 qDebug() << "Now checking for correct exposition of Enums::enum:: pairs."; QJSValue jsEnums = engine->globalObject().property("libXpertMassCore").property("Enums"); if(jsEnums.isUndefined()) { qDebug() << "Error: libXpertMassCore.Enums not found!"; return; } else { qDebug() << "libXpertMassCore.Enums JS object exists"; // Get all enum names (properties of the Enums object) QJSValue propNames = engine->evaluate("Object.getOwnPropertyNames(libXpertMassCore.Enums)"); if(propNames.isError()) { qDebug() << "JS Error:" << propNames.toString(); return; } // Iterate through each enum QStringList enumNames = propNames.toVariant().toStringList(); for(const QString &enumName : enumNames) { QJSValue jsEnum = jsEnums.property(enumName); if(!jsEnum.isObject()) { qDebug() << "Skipping non-object property:" << enumName; continue; } qDebug() << "\nChecking enum:" << enumName; // Get all keys of the iterated enum QJSValue enumKeys = engine->evaluate( QString("Object.getOwnPropertyNames(libXpertMassCore.Enums.%1)") .arg(enumName)); if(enumKeys.isError()) { qDebug() << " JS Error:" << enumKeys.toString(); continue; } // Print key-value pairs for the iterated enum QStringList keys = enumKeys.toVariant().toStringList(); for(const QString &key : keys) { QJSValue value = jsEnum.property(key); qDebug() << " " << key << "=" << value.toInt(); qDebug().noquote() << "\nChecked enum: " << enumName << "member (" << key << " / " << value.toInt() << ")"; } } } #endif } // This function needs to be called this way: // MsXpS::libXpertMassCore::registerGlobalsToQJSEngine(engine_p) // in the program or library that links to this library. void registerGlobalsToQJSEngine(QJSEngine *engine) { QJSValue libraryJsRootProperty; if(engine->globalObject().hasProperty("libXpertMassCore")) { // qDebug() << "Global object property 'libXpertMassCore' already exists."; libraryJsRootProperty = engine->globalObject().property("libXpertMassCore"); } else { // qDebug() << "Global object property 'libXpertMassCore' not found."; // Create a JS object for libXpertMassCore libraryJsRootProperty = engine->newObject(); } // Create a nested JS object for Globals QJSValue js_globals = engine->newObject(); js_globals.setProperty("ATOM_DEC_PLACES", ATOM_DEC_PLACES); js_globals.setProperty("OLIGOMER_DEC_PLACES", OLIGOMER_DEC_PLACES); js_globals.setProperty("POLYMER_DEC_PLACES", POLYMER_DEC_PLACES); js_globals.setProperty("PKA_PH_PI_DEC_PLACES", PKA_PH_PI_DEC_PLACES); libraryJsRootProperty.setProperty("Globals", js_globals); engine->globalObject().setProperty("libXpertMassCore", libraryJsRootProperty); qRegisterMetaType("ErrorList"); } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/globals.cpp000664 001750 001750 00000046442 15100504560 024424 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes /////////////////////// Qt includes #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" int errorListMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::ErrorList"); int errorListPtrMetaTypeId = qRegisterMetaType( "MsXpS::libXpertMassCore::ErrorList *"); namespace MsXpS { namespace libXpertMassCore { /*! \enum MsXpS::libXpertMassCore::Enums::LocationType \ingroup Globals This enum type specified the kind of location: \value INDEX The location is specified as an index, that is, the numbering starts with 0 \value POSITION The location is specified as a position, that is, the numbering starts with 1 */ /*! \variable MsXpS::libXpertMassCore::locationTypeMap \ingroup Globals \brief Map relating each \l{LocationType} member to a corresponding textual representation. */ std::map locationTypeMap{ {Enums::LocationType::INDEX, "INDEX"}, {Enums::LocationType::POSITION, "POSITION"}}; /*! \enum MsXpS::libXpertMassCore::Enums::MassType \ingroup Globals This enum type specifies the type of mass: \value MONO The mass is monoisotopic \value AVG The mass is average \value BOTH (MONO | AVG) */ /*! \variable MsXpS::libXpertMassCore::massTypeMap \ingroup Globals \brief Map relating each \l{MassType} member to a corresponding textual representation. */ std::map massTypeMap{{Enums::MassType::MONO, "MONO"}, {Enums::MassType::AVG, "AVG"}, {Enums::MassType::BOTH, "BOTH"}}; /*! \enum MsXpS::libXpertMassCore::Enums::MassToleranceType \ingroup Globals This enum type specifies the kind of mass tolerance to use for a mass calculation or a mass comparison. \value NONE The tolerance is not specified \value PPM The tolerance is based on parts per million \value MZ The tolerance is based on an absolute m/z value \value AMU The tolerance is based on an absolute mass value \value RES The tolerance is based on resolution \omitvalue LAST */ /*! \variable MsXpS::libXpertMassCore::massToleranceTypeMap \ingroup Globals \brief Map relating each \l{MassToleranceType} member to a corresponding textual representation. */ std::map massToleranceTypeMap{ {Enums::MassToleranceType::NONE, "NONE"}, {Enums::MassToleranceType::PPM, "PPM"}, {Enums::MassToleranceType::MZ, "MZ"}, {Enums::MassToleranceType::AMU, "AMU"}, {Enums::MassToleranceType::RES, "RES"}}; /*! \enum MsXpS::libXpertMassCore::Enums::PolymerEnd \ingroup Globals This enum specifies the polymer end. \value NONE Not defined \value LEFT The left end \value RIGHT The right end \value BOTH (LEFT | RIGHT) */ /*! \variable MsXpS::libXpertMassCore::polymerEndMap \ingroup Globals \brief Map relating each \l{PolymerEnd} member to a corresponding textual representation. */ std::map polymerEndMap{ {Enums::PolymerEnd::NONE, "NONE"}, {Enums::PolymerEnd::LEFT, "LEFT"}, {Enums::PolymerEnd::RIGHT, "RIGHT"}, {Enums::PolymerEnd::BOTH, "BOTH"}}; /*! \enum MsXpS::libXpertMassCore::Enums::IonizationOutcome \ingroup Globals \brief This enum specifies the outcome of an ionization or deionization process. \value FAILED The operation failed. \value UNCHANGED The ionization status did not change. For example calling deionize() on a non-ionized entity returns UNCHANGED. \value IONIZED The ionization was successful. \value DEIONIZED The deionization was successful. */ /*! \variable MsXpS::libXpertMassCore::ionizationOutcomeMap \ingroup Globals \brief Map relating each \l{IonizationOutcome} member to a corresponding textual representation. */ std::map ionizationOutcomeMap{ {Enums::IonizationOutcome::FAILED, "FAILED"}, {Enums::IonizationOutcome::UNCHANGED, "UNCHANGED"}, {Enums::IonizationOutcome::IONIZED, "IONIZED"}, {Enums::IonizationOutcome::DEIONIZED, "DEIONIZED"}}; /*! \enum MsXpS::libXpertMassCore::Enums::SelectionType \ingroup Globals This enum specifies the selection type in a polymer sequence. \value RESIDUAL_CHAINS The selection comprises only residues \value OLIGOMERS The selection comprises oligomers, that is, residual chains capped with the left and right caps. */ /*! \variable MsXpS::libXpertMassCore::selectionTypeMap \ingroup Globals \brief Map relating each \l{SelectionType} member to a corresponding textual representation. */ std::map selectionTypeMap{ {Enums::SelectionType::RESIDUAL_CHAINS, "RESIDUAL_CHAINS"}, {Enums::SelectionType::OLIGOMERS, "OLIGOMERS"}}; /*! \enum MsXpS::libXpertMassCore::Enums::CapType \ingroup Globals This enum specifies the type of cap (the chemical entities that are set to the polymer ends so as to finish its polymerization state from a chain of residues to an actual polymer molecule. \value NONE The cap is not defined \value LEFT The left cap \value RIGHT The right cap \value BOTH (LEFT | RIGHT) */ /*! \variable MsXpS::libXpertMassCore::capTypeMap \ingroup Globals \brief Map relating each \l{CapType} member to a corresponding textual representation. */ std::map capTypeMap{{Enums::CapType::NONE, "NONE"}, {Enums::CapType::LEFT, "LEFT"}, {Enums::CapType::RIGHT, "RIGHT"}, {Enums::CapType::BOTH, "BOTH"}}; /*! \enum MsXpS::libXpertMassCore::Enums::ChemicalEntity \ingroup Globals This enum specifies the monomer chemical entities to account for in a calculation. This enum is typically used when mass calculations need to account or not for the various chemical entities that are attached to a given monomer. \value NONE The chemical entity is not defined. \value MONOMER The monomer \value MODIF The modification \value CROSS_LINKER The cross-linker \value MODIF_AND_CROSS_LINKER The modification and the cross-linker \value CROSS_LINK The cross-link \value LEFT_END_MODIF The polymer left end modification \value FORCE_LEFT_END_MODIF Force accounting of the polymer left end modification even if selection does not encompass the left end monomer \value RIGHT_END_MODIF The polymer right end modification \value FORCE_RIGHT_END_MODIF Force accounting of the polymer right end modification even if selection does not encompass the right end monomer \value BOTH_END_MODIFS Both the polymer left and right ends modifications \value FORCE_BOTH_END_MODIFS Force accounting of both the polymer left and right ends modifications even if selection does not encompass any of the polymer ends monomers */ /*! \variable MsXpS::libXpertMassCore::chemicalEntityMap \ingroup Globals \brief Map relating each \l{ChemicalEntity} member to a corresponding textual representation. */ std::map chemicalEntityMap{ {Enums::ChemicalEntity::NONE, "NONE"}, {Enums::ChemicalEntity::MONOMER, "MONOMER"}, {Enums::ChemicalEntity::MODIF, "MODIF"}, {Enums::ChemicalEntity::CROSS_LINKER, "CROSS_LINKER"}, {Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER, "MODIF AND CROSS_LINKER"}, {Enums::ChemicalEntity::CROSS_LINK, "CROSS_LINK"}, {Enums::ChemicalEntity::LEFT_END_MODIF, "LEFT_END_MODIF"}, {Enums::ChemicalEntity::RIGHT_END_MODIF, "RIGHT_END_MODIF"}, {Enums::ChemicalEntity::FORCE_LEFT_END_MODIF, "FORCE_LEFT_END_MODIF"}, {Enums::ChemicalEntity::RIGHT_END_MODIF, "RIGHT_END_MODIF"}, {Enums::ChemicalEntity::FORCE_RIGHT_END_MODIF, "FORCE_RIGHT_END_MODIF"}, {Enums::ChemicalEntity::BOTH_END_MODIFS, "BOTH_END_MODIFS"}, {Enums::ChemicalEntity::FORCE_BOTH_END_MODIFS, "FORCE_BOTH_END_MODIFS"}}; /*! \enum MsXpS::libXpertMassCore::Enums::CrossLinkEncompassed This enum type specifies the manner in which a sequence region in a \l Polymer or in an \l Oligomer contains fully, or partially not does not contain all the monomers involved in a CrossLink: \value NOT The region does not contain any \l Monomer involved in a CrossLink. \value PARTIALLY The region contains one or more \l{Monomer}s involved in a CrossLink but not all. \value FULLY All the \l{Monomer}s involved in the CrossLink are contained in the \l Polymer or \l Oligomer region. */ /*! \variable MsXpS::libXpertMassCore::crossLinkEncompassedMap \ingroup Globals \brief Map relating each \l{CrossLinkEncompassed} member to a corresponding textual representation. */ std::map crossLinkEncompassedMap{ {Enums::CrossLinkEncompassed::NOT, "NOT"}, {Enums::CrossLinkEncompassed::PARTIALLY, "PARTIALLY"}, {Enums::CrossLinkEncompassed::FULLY, "FULLY"}}; /*! \enum MsXpS::libXpertMassCore::Enums::HashAccountData \ingroup Globals This enum specifies the chemical entites to account for when calculating a hash. \value NOTHING Nothing \value SEQUENCE The sequence \value MONOMER_MODIF Monomer modifications \value POLYMER_MODIF Polymer modifications */ /*! \enum MsXpS::libXpertMassCore::Enums::CleavageAction \ingroup Globals This enum specifies the cleavage operation to be carried out. This enum is typically used when CleavageMotif objects are configured, either for cleavage (after arginine, for Trypsin) or not-for-cleavage, at K/P, for Trypsin). \value NOT_SET The CleavageAction has not been configured \value NO_CLEAVE The opeartion is actual cleavage \value CLEAVE The operation is no cleavage */ /*! \variable MsXpS::libXpertMassCore::CleavageActionMap \ingroup Globals \brief Map relating each \l{CleavageAction} member to a corresponding textual representation. */ std::map CleavageActionMap{ {Enums::CleavageAction::NOT_SET, "NOT_SET"}, {Enums::CleavageAction::NO_CLEAVE, "NO_CLEAVE"}, {Enums::CleavageAction::CLEAVE, "CLEAVE"}}; /*! \enum MsXpS::libXpertMassCore::Enums::FragEnd \ingroup Globals This enum specifies the end of the polymer that is found in a fragment. This enum is typically used to tell what polymer end is found in a fragment ion product. For example, in protein fragmentations, the a, b, c fragments contain the left end of the peptide that was fragmented, while the x, y, z fragments contain the right end of the fragmented peptide. \value NE The fragment contains none of the precursor oligomer ion ends (immonium ions, for example). \value LE The fragment contains the left end of the precursor oligomer ion. \value RE The fragment contains the right end of the precursor oligomer ion. \value BE The fragment contains both the left and the right ends of the precursor oligomer ion. */ /*! \variable MsXpS::libXpertMassCore::fragEndMap \ingroup Globals \brief Map relating each \l{FragEnd} member to a corresponding textual representation. */ std::map fragEndMap{{Enums::FragEnd::NE, "NE"}, {Enums::FragEnd::LE, "LE"}, {Enums::FragEnd::RE, "RE"}, {Enums::FragEnd::BE, "BE"}}; /*! \enum MsXpS::libXpertMassCore::Enums::ChemicalGroupTrapped \ingroup Globals This enum specifies how the chemical group behaves when the chemical entity that it holds polymerizes into a \l Polymer. One example will clear things out: An amino acid has a amino group and a carboxylic acid group. The amino group gets entrapped in the monomer-to-monomer bond (the peptide bond) if the polymerization occurs at the left of the monomer. That means that if the Monomer holding this ChemicalGroup is N-terminal, then the amino group should be accounted for because it is intact. Conversely, the carboxylic acid group gets entrapped in the peptide bond if the polymerization occurs at the right of the monomer. That means that if the Monomer holding this ChemicalGroup is C-terminal, then the carboxylic acid group should be accounted for because it is intact. \value NEVER The chemical group is not lost upon polymerization, it should thus always be accounted for. \value LEFT The chemical group gets trapped in the inter-monomer bond if polymerization occurs at left of the \l Monomer. \value RIGHT The chemical group gets trapped in the inter-monomer bond if polymerization occurs at right of the \l Monomer. */ /*! \variable MsXpS::libXpertMassCore::chemicalGroupTrappedMap \ingroup Globals \brief Map relating each \l{ChemicalGroupTrapped} member to a corresponding textual representation. */ std::map chemicalGroupTrappedMap{ {Enums::ChemicalGroupTrapped::NEVER, "NEVER"}, {Enums::ChemicalGroupTrapped::LEFT, "LEFT"}, {Enums::ChemicalGroupTrapped::RIGHT, "RIGHT"}}; /*! \enum MsXpS::libXpertMassCore::Enums::ChemicalGroupFate \ingroup Globals This enum specifies how the chemical group behaves when the chemical entity that it holds polymerizes into a \l Polymer. This example clarifies the concept: \code C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST \endcode When the Cysteine's amino group is modified because the Cys residue on on the N-terminal end of the polymer, if it gets acetylated, then the amino group is lost because it is trapped in the amide bond. It is thus not accounted for when computing the pI of the protein. \value LOST The chemical group is lost upon modification of the \l Monomer. \value PRESERVED The chemical group is preserved upon modification of the \l Monomer. */ /*! \variable MsXpS::libXpertMassCore::chemicalGroupFateMap \ingroup Globals \brief Map relating each \l{ChemicalGroupFate} member to a corresponding textual representation. */ std::map chemicalGroupFateMap{ {Enums::ChemicalGroupFate::LOST, "LOST"}, {Enums::ChemicalGroupFate::PRESERVED, "PRESERVED"}}; /*! \enum MsXpS::libXpertMassCore::Enums::MassPeakShapeType \ingroup Globals This enum specifies how the peak shape needs to be computed. \value NOT_SET The value is not set. \value GAUSSIAN The peak shape is computed using the Gaussian function. \value LORENTZIAN The peak shape is computed using the Lorentzian function. */ /*! \variable MsXpS::libXpertMassCore::massPeakShapeTypeMap \ingroup Globals \brief Map relating each \l{MassPeakShapeType} member to a corresponding textual representation. */ std::map massPeakShapeTypeMap{ {Enums::MassPeakShapeType::NOT_SET, "NOT_SET"}, {Enums::MassPeakShapeType::GAUSSIAN, "GAUSSIAN"}, {Enums::MassPeakShapeType::LORENTZIAN, "LORENTZIAN"}}; /*! \enum MsXpS::libXpertMassCore::Enums::MassPeakWidthLogic \ingroup Globals This enum specifies how the peak shape needs to be computed. \value NOT_SET The value is not set. \value FWHM The width of the peak shape is determined using the FWHM (full width at half maximum). \value RESOLUTION The width of the peak shape is determined using the resolving power of the instrument. */ /*! \variable MsXpS::libXpertMassCore::massPeakWidthLogicMap \ingroup Globals \brief Map relating each \l{MassPeakWidthLogic} member to a corresponding textual representation. */ std::map massPeakWidthLogicMap{ {Enums::MassPeakWidthLogic::NOT_SET, "NOT_SET"}, {Enums::MassPeakWidthLogic::FWHM, "FWHM"}, {Enums::MassPeakWidthLogic::RESOLUTION, "RESOLUTION"}}; /*! \enum MsXpS::libXpertMassCore::Enums::MassDataType \ingroup Globals This enum type specifies the kind of mass data that are encoded in a CBOR container. \value NOT_SET The mass data type is not set \value MASS_SPECTRUM The mass data type is a mass spectrum \value DRIFT_SPECTRUM The mass data type is a drift spectrum (ion mobility data) \value TIC_CHROMATOGRAM The mass data type is total ion current chromatogram \value XIC_CHROMATOGRAM The mass data type is an extract ion current chromatogram \value MZ_RT_COLORMAP The mass data type is an MZ/RT color map \value DT_RT_COLORMAP The mass data type is a DT/RT color map \value DT_MZ_COLORMAP The mass data type is DT/MZ color map \omitvalue LAST */ /*! \brief Map relating each \l{ MassDataType} member to a corresponding textual representation. */ std::map massDataTypeMap{ {Enums::MassDataType::NOT_SET, "NOT_SET"}, {Enums::MassDataType::MASS_SPECTRUM, "MASS_SPECTRUM"}, {Enums::MassDataType::DRIFT_SPECTRUM, "DRIFT_SPECTRUM"}, {Enums::MassDataType::TIC_CHROMATOGRAM, "TIC_CHROMATOGRAM"}, {Enums::MassDataType::XIC_CHROMATOGRAM, "XIC_CHROMATOGRAM"}, {Enums::MassDataType::MZ_RT_COLORMAP, "MZ_RT_COLORMAP"}, {Enums::MassDataType::DT_RT_COLORMAP, "DT_RT_COLORMAP"}, {Enums::MassDataType::DT_MZ_COLORMAP, "DT_MZ_COLORMAP"}, {Enums::MassDataType::LAST, "LAST"}}; /*! \brief Number of decimal places after the decimal symbol for atom masses. \ingroup Globals */ int ATOM_DEC_PLACES = 10; /*! \brief Number of decimal places after the decimal symbol for oligomer masses. \ingroup Globals */ int OLIGOMER_DEC_PLACES = 6; /*! \brief Number of decimal places after the decimal symbol for polymer masses. \ingroup Globals */ int POLYMER_DEC_PLACES = 3; /*! \brief Number of decimal places after the decimal symbol for pH/pKa values. \ingroup Globals */ int PKA_PH_PI_DEC_PLACES = 2; /*! \typedef MsXpS::libXpertMassCore::ErrorList \relates QString Synonym for std::vector ErrorList. */ } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassCore/src/jsclassregistrar.cpp000664 001750 001750 00000004504 15100504560 026357 0ustar00rusconirusconi000000 000000 #include "MsXpS/libXpertMassCore/jsclassregistrar.h" namespace MsXpS { std::unordered_map & getNameSpaceClassNameJsConstructorRegistrarMap() { static std::unordered_map map; return map; } void registerJsConstructorForEachClassInRegistrarMap(QJSEngine *engine) { qDebug() << "Registering the constructor of the classes marked for JS."; qsizetype count = getNameSpaceClassNameJsConstructorRegistrarMap().size(); qDebug() << "Size of the map: count" << count; for(const auto &entry : getNameSpaceClassNameJsConstructorRegistrarMap()) entry.second(engine); } // Note that name_space can be compounded: MsXpS::libXpertMassCore, for class name Formula, for example. void registerJsConstructorForNameSpaceClassNameInRegistrarMap(const QString &name_space, const QString &class_name, QJSEngine *engine) { // Remember, the registrar map is actually a map of map: // Key type: pair of namespace and class name // using NamespaceClassnamePairAsKey = std::pair; // using RegisterFunc = std::function; // // std::unordered_map // So, first look for a pair // having key = {name_sape, class_name}, that is, a std::pair. Once we get that map item, run the second member of the // outer map item which actually runs the functor. auto it = getNameSpaceClassNameJsConstructorRegistrarMap().find({name_space, class_name}); if(it != getNameSpaceClassNameJsConstructorRegistrarMap().end()) it->second(engine); else qWarning().noquote() << "No JS constructor registered for namespace" << name_space << "and class name" << class_name; } }// namespace MsXps libxpertmass-1.4.0/source/XpertMassCore/xpertmasscore_resources.qrc000664 001750 001750 00000000256 15100504560 027177 0ustar00rusconirusconi000000 000000 ../../doc/javascript/js_reference/Core/class_js_reference_text.txt libxpertmass-1.4.0/source/XpertMassGui/000775 001750 001750 00000000000 15100507137 021333 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/CMakeLists.txt000664 001750 001750 00000016754 15100504560 024105 0ustar00rusconirusconi000000 000000 # set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) # set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES /usr/include) # Whatever the platform, this always works. find_package( Qt6 COMPONENTS Widgets Core Gui PrintSupport Svg Xml Network REQUIRED ) message("\n${BoldGreen}Now configuring library libXpertMassGui${ColourReset}\n") ######################################################## # Files set(XpertMassGui_SRCS # # Utilities src/ColorSelector.cpp src/DecimalPlacesOptionsDlg.cpp src/ToleranceWidget.cpp # # Scripting src/JavaScriptingEnvironment.cpp src/JavaScriptingGuiUtils.cpp src/JavaScriptingWnd.cpp src/JavaScriptWorker.cpp src/ScriptingHistoryListWidget.cpp src/ScriptingObjectTreeWidgetItem.cpp src/JavaScriptQObjectExposureRecord.cpp src/JavaScriptQObjectExposureRegistry.cpp src/XpertMassGuiJavaScript.cpp # # Network src/MassDataClientServerConfigDlg.cpp # # Isotopic cluster and peak shaping src/IsotopicClusterGeneratorDlg.cpp src/IsotopicClusterShaperDlg.cpp src/IsotopicDataTableView.cpp src/IsotopicDataTableViewModel.cpp src/MassPeakShaperConfigDlg.cpp src/MassPeakShaperConfigWidget.cpp ) set(XpertMassGui_UIS src/ui/DecimalPlacesOptionsDlg.ui src/ui/IsotopicClusterGeneratorDlg.ui src/ui/MassPeakShaperConfigDlg.ui src/ui/IsotopicClusterShaperDlg.ui src/ui/ElementGroupBoxWidget.ui src/ui/MassDataClientServerConfigDlg.ui src/ui/MassPeakShaperConfigWidget.ui src/ui/ToleranceWidget.ui src/ui/JavaScriptingWnd.ui ) qt6_wrap_ui(XpertMassGui_UIS_H ${XpertMassGui_UIS}) qt6_add_resources(XpertMassGui_QRC_CPP xpertmassgui_resources.qrc) # Because the header files are in their own directory and not along the source # files, we need to have them listed explicitely for automoc to work properly # below. # Create a variable to hold the 'includes' directory *relative* to the # current CMakeLists.txt file. set(INCLUDES_DIR "${CMAKE_CURRENT_LIST_DIR}/includes") file(GLOB_RECURSE XpertMassGui_HEADERS ${INCLUDES_DIR}/*.h ${INCLUDES_DIR}/*.hpp) message(STATUS "Included the header files from ${INCLUDES_DIR}: \n\ ${XpertMassGui_HEADERS}" ) # Instruct CMake to run moc automatically when needed. set(CMAKE_AUTOMOC ON) ############################################################### # Configuration of the binary to be built ############################################################### # Only now can we add the library, because we have defined the sources. ############################################################# # Build the static lib add_library( Gui_static STATIC ${XpertMassGui_HEADERS} ${XpertMassGui_SRCS} ${XpertMassGui_UIS_H} ${XpertMassGui_QRC_CPP} ${PLATFORM_SPECIFIC_SOURCES} ) set_target_properties( Gui_static PROPERTIES # Set target properties for namespace EXPORT_NAME "Gui_static" OUTPUT_NAME XpertMassGui LINK_FLAGS "-Wl,--whole-archive" ) target_link_libraries( Gui_static PUBLIC PappsoMSpp::Core PappsoMSpp::Gui IsoSpec++::IsoSpec++ Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport Qt6::Svg Qt6::Xml Qt6::Network QScintilla::QScintilla Qt6::Qml Core_static ) # The INSTALL_INTERFACE that is ${prefix}/include target_include_directories( Gui_static PUBLIC # These include directories are for building of this lib. $ $ $ # These include directories are for users of this lib. $ ) get_target_property(Gui_static_INCLUDES Gui_static INCLUDE_DIRECTORIES) message(STATUS "Gui_static_INCLUDES: ${Gui_static_INCLUDES}") install( TARGETS Gui_static EXPORT XpertMassGuiStaticTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Export static targets install( EXPORT XpertMassGuiStaticTargets FILE XpertMassGuiStaticTargets.cmake NAMESPACE XpertMass:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass ) export( EXPORT XpertMassGuiStaticTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/XpertMassGuiStaticTargets.cmake" NAMESPACE XpertMass:: ) ############################################################# # Build the shared lib add_library( Gui SHARED ${XpertMassGui_HEADERS} ${XpertMassGui_SRCS} ${XpertMassGui_UIS_H} ${XpertMassGui_QRC_CPP} ${PLATFORM_SPECIFIC_SOURCES} ) set_target_properties( Gui PROPERTIES # Set target properties for namespace EXPORT_NAME "Gui" OUTPUT_NAME XpertMassGui VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} LINK_FLAGS "-Wl,--no-as-needed" POSITION_INDEPENDENT_CODE ON ) target_link_libraries( Gui PUBLIC PappsoMSpp::Core PappsoMSpp::Gui IsoSpec++::IsoSpec++ Qt6::Core Qt6::Gui Qt6::Widgets Qt6::PrintSupport Qt6::Svg Qt6::Xml Qt6::Network QScintilla::QScintilla Qt6::Qml Core ) # The install interface that is ${prefix}/include target_include_directories( Gui PUBLIC # These include directories are for building of this lib. $ $ $ # These include directories are for users of this lib. $ ) get_target_property(Gui_INCLUDES Gui INCLUDE_DIRECTORIES) message(STATUS "Gui_INCLUDES: ${Gui_INCLUDES}") target_compile_definitions(Gui PRIVATE "EXPORT_LIB_SYMBOLS") # This is to avoid the "include_next(math.h) file not found" error. if(WIN32) set_target_properties(Gui_static PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) set_target_properties(Gui PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) endif() install( TARGETS Gui EXPORT XpertMassGuiSharedTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Export shared targets install( EXPORT XpertMassGuiSharedTargets FILE XpertMassGuiSharedTargets.cmake NAMESPACE XpertMass:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/XpertMass ) export( EXPORT XpertMassGuiSharedTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/XpertMassGuiSharedTargets.cmake" NAMESPACE XpertMass:: ) ############################################################# # Common installation configuration message("The directory containing the includes: ${CMAKE_CURRENT_LIST_DIR}/includes/MsXpS/libXpertMassGui") install( DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/includes/MsXpS/libXpertMassGui DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/MsXpS" ) message("") message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}") message("") message(STATUS "${BoldGreen}Finished configuration of libXpertMassGui.${ColourReset}") message("") libxpertmass-1.4.0/source/XpertMassGui/includes/000775 001750 001750 00000000000 15100507137 023141 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/000775 001750 001750 00000000000 15100507137 024153 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/000775 001750 001750 00000000000 15100507137 027235 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/ColorSelector.hpp000664 001750 001750 00000005422 15100504560 032525 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2019 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { struct colorCompare { bool operator()(const QColor &a, const QColor &b) const { // The convention is to return a < b; // Here I decide that the comparison work is like this: // // get the HSV representation of the color, and sequentially test H, S and V // for inequality between colors a and b. int h_a; int s_a; int v_a; a.getHsv(&h_a, &s_a, &v_a); int h_b; int s_b; int v_b; b.getHsv(&h_b, &s_b, &v_b); if(h_a < h_b) return true; if(s_a < s_b) return true; if(v_a < v_b) return true; return false; } }; using ColorUsagePair = std::pair; class DECLSPEC ColorSelector { public: ColorSelector(); virtual ~ColorSelector(); void addColor(const QColor &color); static QColor getColor(bool make_random = true); static QColor chooseColor(bool allow_used = false); static QColor getRandomColor(); static QColor getColor(const QColor &color); static void releaseColor(const QColor &color); static bool isUsedColor(const QColor &color); protected: static std::vector m_usageOfColors; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/DecimalPlacesOptionsDlg.hpp000664 001750 001750 00000006267 15100504560 034447 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class DecimalPlacesOptionsDlg; } namespace MsXpS { namespace libXpertMassGui { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: DecimalPlacesOptionsDlg * * BEGIN DESCRIPTION * This class allows the user to configure the number of decimals * to be used when textually representing the mass values of various * chemical entities. * END DESCRIPTION */ class DECLSPEC DecimalPlacesOptionsDlg: public QDialog { Q_OBJECT Q_PROPERTY(int atomDecPlaces MEMBER m_atomDecPlaces) Q_PROPERTY(int oligomerDecPlaces MEMBER m_oligomerDecPlaces) Q_PROPERTY(int polymerDecPlaces MEMBER m_polymerDecPlaces) Q_PROPERTY(int pkaPhPiDecPlaces MEMBER m_pkaPhPiDecPlaces) public: DecimalPlacesOptionsDlg(QWidget *, const QString &configSettingsFilePath); ~DecimalPlacesOptionsDlg(); Q_INVOKABLE void updateDecimalPlaces(int atom, int oligo, int poly, int pka_ph); static void registerJsConstructor(QJSEngine *engine); public slots: void validate(); signals: void decimalPlacesOptionsChangedSignal(); private: Ui::DecimalPlacesOptionsDlg *mp_ui; QString m_configSettingsFilePath; int m_atomDecPlaces = 10; int m_oligomerDecPlaces = 6; int m_polymerDecPlaces = 3; int m_pkaPhPiDecPlaces = 2; void closeEvent(QCloseEvent *event); bool fetchValidateInputData(); }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: DecimalPlacesOptionsDlg */ } // namespace libXpertMassGui MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassGui, DecimalPlacesOptionsDlg) } // namespace MsXpS source/XpertMassGui/includes/MsXpS/libXpertMassGui/IsotopicClusterGeneratorDlg.hpp000664 001750 001750 00000014453 15100504560 035324 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes #include #include #include /////////////////////// Qt includes #include #include #include #include #include ////////////////////// pappso includes #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Formula.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/IsotopicDataTableViewModel.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class IsotopicClusterGeneratorDlg; } namespace MsXpS { namespace libXpertMassGui { class IsotopicDataTableViewModel; class IsotopicDataTableViewSortProxyModel; class DECLSPEC IsotopicClusterGeneratorDlg : public QDialog { Q_OBJECT public: IsotopicClusterGeneratorDlg(QWidget *program_window_p, const QString &applicationName, const QString &description); virtual ~IsotopicClusterGeneratorDlg(); bool initializeUserManualConfigurationWidgets( const libXpertMassCore::IsotopicDataManualConfigHandler &config_handler); public slots: // std::pair addElementSkeletonGroupBox(); QGroupBox *addElementSkeletonGroupBox(); QGroupBox *addElementGroupBox(); void removeElementGroupBox(); QFrame *createIsotopeFrame(QGroupBox *elementGroupBox = nullptr); std::pair addIsotopeFrame(); void removeIsotopeFrame(); /////////////////////// The run functions ////////////////////// /// These function are adapted to the three different contexts // Running IsoSpec computations based on the static library isotopic data bool runLibrary(); // Running IsoSpec computations based on the user config isotopic data bool runUserConfig(); // Running IsoSpec computations based on the manual config isotopic data bool runUserManualConfig(); void toIsotopicClusterShaper(); void addLibraryFormula(); void removeLibraryFormula(); void addUserConfigFormula(); void removeUserConfigFormula(); void traceColorPushButtonClicked(); signals: void displayMassSpectrumSignal(const QString &title, const QByteArray &color_byte_array, pappso::TraceCstSPtr trace); protected: Ui::IsotopicClusterGeneratorDlg *mp_ui; QString m_applicationName; QString m_windowDescription; libXpertMassCore::Formula m_formula; QWidget *mp_programWindow = nullptr; int m_normalizeIntensity = std::numeric_limits::min(); double m_maxSummedProbability = 0.95; int m_charge = 1; pappso::Enums::SortType m_sortType = pappso::Enums::SortType::none; pappso::Enums::SortOrder m_sortOrder = pappso::Enums::SortOrder::ascending; ///////////// Isotopic data for three different contexts //////////// // Isotopic data from the IsoSpec library tables libXpertMassCore::IsotopicDataSPtr msp_isotopicDataLibrary = nullptr; // Isotopic data from the user's personal file libXpertMassCore::IsotopicDataSPtr msp_isotopicDataUserConfig = nullptr; // Isotopic data from the user's personal GUI-based manual config file libXpertMassCore::IsotopicDataSPtr msp_isotopicDataUserManualConfig = nullptr; ///////////// Table view models for two different contexts //////////// // The table view model that manages the library static IsoSpec standard data IsotopicDataTableViewModel *mpa_libraryStaticTableViewModel; // The table view model that manages the user config IsoSpec standard data IsotopicDataTableViewModel *mpa_userStaticTableViewModel; // For the mass spectra that are synthesized and served. QByteArray m_colorByteArray; // Note that there is no model for the user manual config data because this // configuration is not entered in a table view but using another paradigm. libXpertMassCore::IsotopicClusterGenerator m_clusterGenerator; void closeEvent(QCloseEvent *event); void writeSettings(const QString &configSettingsFilePath); void readSettings(const QString &configSettingsFilePath); std::size_t validateManualConfig( libXpertMassCore::IsotopicDataManualConfigHandler &config_handler); void setupIsoSpecStandardStaticTableView(); void setupIsoSpecStandardUserTableView(); bool setupDialog(); bool loadLibrary(); bool saveLibrary(); bool loadUserConfig(); bool saveUserConfig(); bool loadUserManualConfig(); bool saveUserManualConfig(); bool checkParameters(); void reportResults(); void message(const QString &message, int timeout = 3000); }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/IsotopicClusterShaperDlg.hpp000664 001750 001750 00000013001 15100504560 034663 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes #include #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class IsotopicClusterShaperDlg; } namespace MsXpS { namespace libXpertMassGui { class ProgramWindow; class DECLSPEC IsotopicClusterShaperDlg : public QDialog { Q_OBJECT public: IsotopicClusterShaperDlg(QWidget *program_window_p, const QString &applicationName, const QString &description); IsotopicClusterShaperDlg( QWidget *program_window_p, const QString &applicationName, const QString &description, pappso::TraceCstSPtr isotopic_cluster_sp, int normalizing_intensity = std::numeric_limits::min()); virtual ~IsotopicClusterShaperDlg(); void setIsotopiCluster(pappso::TraceCstSPtr isotopic_cluster_sp); // The normalizing intensity usually is the greatest intensity in the cluster // centroids that is to be used for normalization of the peaks in the cluster. void setNormalizingIntensity(double new_max_intensity); void setMassSpectrumTitle(const QString &title); void setColorByteArray(QByteArray color_byte_array); enum class TabWidgetPage { INPUT_DATA = 0x000, LOG, RESULTS, }; public slots: void traceColorPushButtonClicked(); signals: void displayMassSpectrumSignal(const QString &title, const QByteArray &color_byte_array, pappso::TraceCstSPtr trace); protected: Ui::IsotopicClusterShaperDlg *mp_ui; MassPeakShaperConfigWidget *mp_massPeakShaperConfigWidget = nullptr; QWidget *mp_programWindow = nullptr; QString m_applicationName; QString m_description; QString m_fileName; pappso::MapTrace m_mapTrace; pappso::Trace m_finalTrace; // For the mass spectra that are synthesized and served. QByteArray m_colorByteArray; pappso::MzIntegrationParams m_mzIntegrationParams; // Of all the peak centroids' intensities, what is the m/z value of the most // intense? double m_referencePeakMz = 0.0; int m_normalizingIntensity = 1; // This is the starting material for the shaping. Each peak centroid in this // Trace is the apex of a peak to be shaped. There can be as many peak // centroids a desired, they will all be treated separately. pappso::Trace m_isotopicCluster; // Each (m/z,i) pair (the i is in fact a probability that can later be // converted to a relative intensity) in the text edit widget will be // converted into a PeakShaper instance. Each PeakShaper instance will craft // the m_pointCount DataPoints to create the trace corresponding to the // starting peak centroid. QList m_peakShapers; libXpertMassCore::MassPeakShaperConfig m_config; std::shared_ptr msp_msgTimer = std::make_shared(); void writeSettings(const QString &configSettingsFilePath); void readSettings(const QString &configSettingsFilePath); void closeEvent(QCloseEvent *event); void setupDialog(); QString craftMassSpectrumName(); std::size_t fillInThePeakShapers(); void colorizePushButton(QPushButton *push_button_p, QByteArray color_byte_array); void message(const QString &message, int timeout = 3000); private slots: void importFromText(); bool checkParameters(); bool checkParameters(libXpertMassCore::ErrorList &error_list); // void inputDataSelectionChanged(); void run(); void outputFileName(); void displayMassSpectrum(); void copyMassSpectrumToClipboard(); void normalizingGrouBoxToggled(bool checked); void normalizingIntensityValueChanged(); }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/IsotopicDataTableView.hpp000664 001750 001750 00000005342 15100504560 034135 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Std lib includes #include /////////////////////// Qt includes #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class IsotopicClusterGeneratorDlg; class DECLSPEC IsotopicDataTableView : public QTableView { Q_OBJECT public: IsotopicDataTableView(QWidget *parent = 0); ~IsotopicDataTableView(); void setIsotopicData(libXpertMassCore::IsotopicDataSPtr isotopic_data_sp); libXpertMassCore::IsotopicDataSPtr getIsotopicData(); QWidget *parent(); void setParent(QWidget *parent_p); void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); signals: void selectionChangedSignal(const QItemSelection &selected, const QItemSelection &deselected); void allSelectedIndicesSignal(QModelIndexList selected_indices); void selectedRowsChangedSignal(std::set selected_rows); protected: QWidget *mp_parent; libXpertMassCore::IsotopicDataSPtr msp_isotopicData; // For drag operations. QPoint m_dragStartPos; // We do not want to hide the parent class function here. using QAbstractItemView::startDrag; void startDrag(); }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/IsotopicDataTableViewModel.hpp000664 001750 001750 00000006307 15100504560 035120 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/IsotopicDataTableView.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class IsotopicClusterGeneratorDlg; class DECLSPEC IsotopicDataTableViewModel : public QAbstractTableModel { Q_OBJECT public: IsotopicDataTableViewModel(QObject *object, libXpertMassCore::IsotopicDataSPtr isotopic_data_sp); virtual ~IsotopicDataTableViewModel(); virtual const QWidget *parent() const; void setTableView(IsotopicDataTableView *); IsotopicDataTableView *tableView(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int, Qt::Orientation, int role = Qt::DisplayRole) const override; bool insertRow(int row, const QModelIndex &parent = QModelIndex()); QVariant data(const QModelIndex &parent = QModelIndex(), int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; void refreshAfterNewData(); bool clearAllData(); Qt::ItemFlags flags(const QModelIndex &index) const override; void editCompleted(const QString &text); void insertIsotopeRowAbove(); void insertIsotopeRowBelow(); void removeSelected(); signals: void modifiedSignal(); protected: QWidget *mp_parent = nullptr; libXpertMassCore::IsotopicDataSPtr msp_isotopicData; IsotopicDataTableView *mp_tableView; signals: void editCompletedSignal(const QString &text); }; } // namespace libXpertMassGui } // namespace MsXpS source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptQObjectExposureRecord.hpp000664 001750 001750 00000004537 15100504560 036105 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once #include #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class DECLSPEC JavaScriptQObjectExposureRecord { public: JavaScriptQObjectExposureRecord( QObject *object_p, QObject *object_parent_p = nullptr, const QString &object_name = QString(), const QString &object_alias = QString(), const QString &object_description = QString()); virtual ~JavaScriptQObjectExposureRecord(); QObject *getObject(); QObject *getObjectParent(); QString getObjectName(); QString getObjectAlias(); QString getObjectDescription(); protected: QObject *mp_object; QObject *mp_objectParent; QString m_objectName; QString m_objectAlias; QString m_objectDescription; }; typedef QSharedPointer JavaScriptQObjectExposureRecordQSPtr; } // namespace libXpertMassGui } // namespace MsXpS source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptQObjectExposureRegistry.hpp000664 001750 001750 00000005175 15100504560 036476 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes #include #include /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/JavaScriptQObjectExposureRecord.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class DECLSPEC JavaScriptQObjectExposureRegistry: public QObject { Q_OBJECT public: JavaScriptQObjectExposureRegistry(QObject *parent = nullptr); virtual ~JavaScriptQObjectExposureRegistry(); int registerQObject(QObject *object_p, QObject *object_parent_p, const QString &object_name, const QString &object_alias, const QString &object_decription); int unregisterQObject(QObject *object_p); JavaScriptQObjectExposureRecordQSPtr getRecordByObject(QObject *object_p); JavaScriptQObjectExposureRecordQSPtr getRecordByObjectParent(QObject *object_p); JavaScriptQObjectExposureRecordQSPtr getRecordByName(const QString object_name); JavaScriptQObjectExposureRecordQSPtr getRecordByAlias(const QString object_alias); protected: QList m_records; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptWorker.hpp000664 001750 001750 00000005062 15100504560 033206 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include #include #include #include /////////////////////// libXpertMassGUI includes #include #include #include #include "MsXpS/libXpertMassGui/JavaScriptingEnvironment.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingGuiUtils.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class DECLSPEC JavaScriptWorker : public QObject { Q_OBJECT public: JavaScriptWorker(QString script, const JavaScriptingEnvironment *env); public slots: void run(); void requestAbort(); signals: void finishedSignal(QJSValue result); private: QString m_script; const JavaScriptingEnvironment *mp_scriptingEnvironment = nullptr; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptingEnvironment.hpp000664 001750 001750 00000011045 15100504560 034735 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once #include #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/JavaScriptQObjectExposureRegistry.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { //! Kind of entity handled in a JavaScript object creation operation. /*! * When creating JavaScript objects, define which kind of entity is being * created. * * \sa msXpSlibXpertMassGui::JavaScriptingWnd * \sa msXpSlibXpertMassGui::ScriptingObjectTreeWidgetItem */ enum ScriptEntityType { UndefinedEntity, StringEntity, FunctionEntity, ArrayOrListEntity, QObjectEntity, IntegerEntity, DoubleEntity }; class JavaScriptingWnd; class JavaScriptingGuiUtils; class DECLSPEC JavaScriptingEnvironment: public QObject { Q_OBJECT public: explicit JavaScriptingEnvironment(QObject *parent = nullptr); virtual ~JavaScriptingEnvironment(); bool initializeJsEngine() const; // Evaluate JavaScript code Q_INVOKABLE QJSValue evaluate(const QString &script); Q_INVOKABLE QJSValue evaluate(QJSEngine *engine_p, const QString &script); Q_INVOKABLE QJSEngine *getJsEngine() const; Q_INVOKABLE bool exposeQObject(QJSEngine *js_engine_p, const QString &name, const QString &alias, const QString &description, QObject *object_p, QObject *object_parent_p, QJSValue &returned_js_value, QJSEngine::ObjectOwnership ownership = QJSEngine::ObjectOwnership::CppOwnership) ; Q_INVOKABLE bool exposeQObject(const QString &name, const QString &alias, const QString &description, QObject *object_p, QObject *object_parent_p, QJSValue &returned_js_value, QJSEngine::ObjectOwnership ownership = QJSEngine::ObjectOwnership::CppOwnership); Q_INVOKABLE bool exposeJsValueToJsEngine(const QString &name, QJSValue &js_value) const; QJSValue getExposedJsEngineObjectByName(const QString &name) const; void registerJsConstructorForClassInRegistrarMap( const QString &name_space, const QString &class_name, QJSEngine *js_engine_p = nullptr) const; void registerJsConstructorForClassesInRegistrarMap( const std::vector> &namespace_class_pairs, QJSEngine *js_engine_p = nullptr) const; void registerJsConstructorForEachClassInRegistrarMap( QJSEngine *js_engine_p = nullptr) const; void registerOwnJsEntities(QJSEngine *js_engine_p = nullptr) const; void registerExternalJsEntities(const QString &name_space, QJSEngine *js_engine_p = nullptr) const; signals: void initializeJsEngineSignal(QJSEngine *js_engine_p) const; void errorOccurredSignal(const QString &errorMessage) const; private: QJSEngine *mp_jsEngine; JavaScriptQObjectExposureRegistry m_registry; JavaScriptingWnd *mp_javaScriptingWnd; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptingGuiUtils.hpp000664 001750 001750 00000004054 15100504560 034200 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class JavaScriptingWnd; class DECLSPEC JavaScriptingGuiUtils : public QObject { Q_OBJECT public: explicit JavaScriptingGuiUtils(QWidget *parent = nullptr); virtual ~JavaScriptingGuiUtils(); Q_INVOKABLE void print(const QString &text); Q_INVOKABLE void printToFile(const QString &text, const QString &file_name); Q_INVOKABLE void msleep(int milliseconds) const; Q_INVOKABLE void sleep(int seconds) const; Q_INVOKABLE void processEvents(); protected: JavaScriptingWnd *mp_javaScriptingWnd = nullptr; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/JavaScriptingWnd.hpp000664 001750 001750 00000017142 15100504560 033165 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// StdLib includes /////////////////////// Qt includes #include #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/globals.hpp" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/JavaScriptingGuiUtils.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingEnvironment.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class JavaScriptingWnd; } namespace MsXpS { namespace libXpertMassGui { enum HistoryLoggingType { Never = 0x0000, OnSuccess = 1 << 0, OnFailure = 1 << 1, Always = OnSuccess | OnFailure }; enum LogTextColor { Comment, Success, Undefined, Failure, JsInput }; class ScriptingObjectTreeWidgetItem; class DECLSPEC JavaScriptingWnd : public QMainWindow { Q_OBJECT public: enum LogDestination { ScriptInput, ScriptOutput, History }; enum ObjectTreeWidgetColumn { Name = 0, Alias, Last }; enum StackedJsRefWidgetPage { PappsoMSpp = 0, XpertMass = 1, XpertMassGui = 2, }; // Construction/Destruction JavaScriptingWnd(const QString &application_name, QMainWindow *parent = nullptr); ~JavaScriptingWnd(); void writeSettings(); void readSettings(); void initialize(); JavaScriptingEnvironment *getScriptingEnvironment(); const QColor m_defaultColor = QColor("black"); QColor m_successColor = QColor("green"); QColor m_undefinedColor = QColor("black"); QColor m_failureColor = QColor("red"); QColor m_commentColor = QColor("gray"); QColor m_jsInputColor = QColor("black"); QMap m_logTextColorMap; QString m_commentPrefix = "\t"; QWidget *mp_parentWnd = 0; QString m_inScriptFileName; QString m_outScriptFileName; QString m_history; bool wasSelecting = false; int m_lastHistoryIndex = -1; int m_historyAnchorIndex = -1; HistoryLoggingType m_historyLoggingType = HistoryLoggingType::Always; void setHistoryLoggingType(HistoryLoggingType type); HistoryLoggingType historyLoggingType() const; Q_INVOKABLE int restoreHistory(QSettings &settings); Q_INVOKABLE void resetHistory(); Q_INVOKABLE void saveHistory(); Q_INVOKABLE void showHistory(); Q_INVOKABLE QString historyAsString( bool colorTagged = false, const QString &lineSeparator = QString("\n")); int restoreHistoryLoggingType(QSettings &settings); void initializeJsEngine(QJSEngine *js_engine_p); QColor logColor(LogTextColor contextColor = LogTextColor::Comment); void logOutTextEdit(const QString &text, LogTextColor color = LogTextColor::Comment); void feedBackForExposedQObject(const QString &object_name, const QString &object_alias, const QString &object_description); // Set appendToList to false to avoid appending the text to m_historyList. // Do so only carefully, that is, typically when calling this function // initially set the history text items in the listwidget and textedit // from the settings. void logHistory(const QString &text, LogTextColor color = LogTextColor::Comment); QString &prependColorTag(QString &text, LogTextColor color = LogTextColor::Comment); bool removeColorTag(QString &text, LogTextColor &color); Q_INVOKABLE void runScript(const QString &script, const QString &comment = QString()); Q_INVOKABLE void loadScriptFile(const QString &fileName = QString()); Q_INVOKABLE void show(); Q_INVOKABLE void hide(); int verbatimFilterJsReferenceText(const QString *input_text_p, QString *filtered_text_p); int regexFilterJsReferenceText(const QString *input_text_p, QString *filtered_text_p); public slots: // History list widget ////// void historyListWidgetCurrentRowChanged(int row); void historyListWidgetItemActivated(); void historyLoggingCheckBoxStateChanged(); void historyRegExpLineEditReturnPressed(); void jsRefTextSearchLineEditReturnPressed(); void codeTextToCodeEditor(const QString code_text, bool overwrite); void scriptHasFinishedRunning(QJSValue value); signals: protected: QsciScintilla *mp_codeEditor = nullptr; QString m_applicationName; QString m_scriptText; QString m_scriptComment; JavaScriptingGuiUtils *mp_javaScriptingGuiUtils = nullptr; JavaScriptingEnvironment *mp_scriptingEnvironment; QMap m_mapJsPropNameTreeWidgetItem; QMenu *mp_fileMenu; QAction *mp_loadScriptFileAct; bool m_shouldHistoryLoadCancel = false; Ui::JavaScriptingWnd *mp_ui; /////////////////// JavaScript reference text handling /////////////////// /////////////////// JavaScript reference text handling /////////////////// // This map relates the name of the software piece and the JS reference // text that relates to it. For example "libpappsomspp" or "libXpertMass" // or "libXpertMassGui". QMap m_jsRefLabelRefTextMap; // This function sets the JS reference text for *this library's flibXpertMass // and libXpertMassGui. void registerKnownJsReferenceTexts(); // Add a new label/js_ref_text pair to the map. Any previous pair by the // same key is lost. void registerJsReferenceText(const QString &label, const QString &text); // Creates a new tab, sets its label and sets the JS reference text // as found as value of the label key. void displayMappedJsRefTextToTab(const QString &label); // Iterates in the m_jsRefLabelRefTextMap and for each found pair, // creates a new tab, sets its label and sets the JS reference text. void displayAllMappedJsRefTextsToTabs(); int regexFilterJsReferenceText(const QString &input_text, QString &filtered_text); int verbatimFilterJsReferenceText(const QString &input_text, QString &filtered_text); void closeEvent(QCloseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; QStringList historyAsStringList(bool colorTagged = true); }; } // namespace libXpertMassGui } // namespace MsXpS source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassDataClientServerConfigDlg.hpp000664 001750 001750 00000007615 15100504560 035475 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class MassDataClientServerConfigDlg; } namespace MsXpS { namespace libXpertMassGui { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassDataClientServerConfigDlg */ class DECLSPEC MassDataClientServerConfigDlg: public QDialog { Q_OBJECT Q_PROPERTY(std::pair serverIpConfig MEMBER m_serverIpConfig) Q_PROPERTY(std::pair clientIpConfig MEMBER m_clientIpConfig) public: MassDataClientServerConfigDlg(QWidget *parent, const QString &applicationName, const QString &description, std::pair server_ip_config = std::pair("", 0)); virtual ~MassDataClientServerConfigDlg(); void updateServerIpData(std::pair server_ip_config); void updateClientIpData(std::pair client_ip_config); void updateClientConfigurationData(const QString &ip_address, int port_number); void clientFailingFeedback(const QString &error); void message(const QString &message, int timeout = 3000); static void registerJsConstructor(QJSEngine *engine); signals: void startServerSignal(); void startClientSignal(const QString ip_address, int port_number); void stopServerSignal(); void stopClientSignal(); private slots: void startClient(); protected: QString m_applicationName; QWidget *mp_programWindow = nullptr; // Describes the connection data for any client to connect to the server that // is started in this dialog window. std::pair m_serverIpConfig = std::pair("", 0); // Described the connection data for the client started in this dialog window // to connect to the server. QPair m_clientIpConfig = std::pair("", 0); Ui::MassDataClientServerConfigDlg *mp_ui; void closeEvent(QCloseEvent *event); void writeSettings(const QString &configSettingsFilePath); void readSettings(const QString &configSettingsFilePath); }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassDataClientServerConfigDlg */ } // namespace libXpertMassGui MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassGui, MassDataClientServerConfigDlg) } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassPeakShaperConfigDlg.hpp000664 001750 001750 00000007561 15100504560 034400 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes #include #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class MassPeakShaperConfigDlg; } namespace MsXpS { namespace libXpertMassGui { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassPeakShaperConfigDlg */ class DECLSPEC MassPeakShaperConfigDlg: public QDialog { Q_OBJECT public: MassPeakShaperConfigDlg(QWidget *program_window_p, const QString &applicationName, const QString &description); virtual ~MassPeakShaperConfigDlg(); void writeSettings(const QString &configSettingsFilePath); void readSettings(const QString &configSettingsFilePath); // The normalizing intensity usually is the greatest intensity in the cluster // centroids that is to be used for normalization of the peaks in the cluster. // Q_INVOKABLE void setNormalizingIntensity(double new_max_intensity); Q_INVOKABLE void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); Q_INVOKABLE MassPeakShaperConfigWidget *getConfigWidget(); static void registerJsConstructor(QJSEngine *engine); protected: Ui::MassPeakShaperConfigDlg *mp_ui; MassPeakShaperConfigWidget *mp_massPeakShaperConfigWidget = nullptr; QWidget *mp_parent = nullptr; QString m_applicationName; libXpertMassCore::MassPeakShaperConfig m_config; void closeEvent(QCloseEvent *event); void setupDialog(); signals: void updatedMassPeakShaperConfigSignal( const libXpertMassCore::MassPeakShaperConfig &config); }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassPeakShaperConfigDlg */ MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassGui, MassPeakShaperConfigDlg) } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp000664 001750 001750 00000011742 15100504560 035111 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes /////////////////////// Qt includes #include #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/globals.hpp" /////////////////////// libXpertMassGUI includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class MassPeakShaperConfigWidget; } namespace MsXpS { namespace libXpertMassGui { /* BEGIN CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassPeakShaperConfigWidget */ class DECLSPEC MassPeakShaperConfigWidget: public QWidget { Q_OBJECT Q_PROPERTY(double referencePeakMz MEMBER m_referencePeakMz) Q_PROPERTY( libXpertMassCore::MassPeakShaperConfig *config READ getConfig MEMBER mp_config) Q_PROPERTY(double normalizingIntensity MEMBER m_normalizingIntensity) public: explicit MassPeakShaperConfigWidget(const libXpertMassCore::MassPeakShaperConfig &config, QWidget *parent_p); virtual ~MassPeakShaperConfigWidget(); void writeSettings(const QString &configSettingsFilePath); void readSettings(const QString &configSettingsFilePath); void setReferencePeakMz(double mz); double getReferencePeakMz(); Q_INVOKABLE libXpertMassCore::MassPeakShaperConfig *getConfig(); QSpinBox *getResolutionSpinBox(); QDoubleSpinBox *getFwhmDoubleSpinBox(); QSpinBox *getFmwhToBinSizeDivisorSpinBox(); QSpinBox *getPointCountSpinBox(); QRadioButton *getGaussianRadioButton(); QRadioButton *getLorentzianRadioButton(); // This function needs to be public because it is necessary to the user of // this widget when checking correctness of the parameters. Q_INVOKABLE bool processBinSizeConfig(); Q_INVOKABLE void setParameters(double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor = 6, bool should_create_bins = true); Q_INVOKABLE void setBinSizeManually(double bin_size, bool should_create_bins = false); Q_INVOKABLE bool checkParameters(); Q_INVOKABLE bool checkParameters(libXpertMassCore::ErrorList *error_list); Q_INVOKABLE const libXpertMassCore::MassPeakShaperConfig *getConfig() const; static void registerJsConstructor(QJSEngine *engine); public slots: void resolutionEditingFinished(); void fwhmEditingFinished(); void fwhmBinSizeDivisorValueChanged(int value); void pointCountEditingFinished(); void gaussianRadioButtonToggled(bool checked); void lorentzianRadioButtonToggled(bool checked); void referencePeakMzEdited(const QString &text); void noBinsCheckBoxStateChanged(int state); signals: void updatedMassPeakShaperConfigSignal( const libXpertMassCore::MassPeakShaperConfig &config); protected: Ui::MassPeakShaperConfigWidget *mp_ui; QWidget *mp_parent = nullptr; double m_referencePeakMz = 0.0; libXpertMassCore::MassPeakShaperConfig *mp_config = nullptr; double m_normalizingIntensity = std::numeric_limits::min(); std::shared_ptr msp_msgTimer = std::make_shared(); void setupWidget(); void message(const QString &message, int timeout = 3000); }; /* END CLASS JS REFERENCE * namespace: MsXpS::libXpertMassGui * class name: MassPeakShaperConfigWidget */ MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassGui, MassPeakShaperConfigWidget) } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/ScriptingHistoryListWidget.hpp000664 001750 001750 00000004172 15100504560 035273 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class DECLSPEC ScriptingHistoryListWidget : public QListWidget { Q_OBJECT public: ScriptingHistoryListWidget(QWidget *parent = 0); ~ScriptingHistoryListWidget(); QString selectedItemsText(); void deselectAll(); private: void keyPressEvent(QKeyEvent *event); signals: void setSelectedItemsTextToScriptInput(const QString &code_text, bool overwrite); public slots: void showAll(); void toggleSelection(); void hideSelected(); void removeSelected(); void removeDuplicates(); void overwriteSelectedItemsText(); void insertSelectedItemsText(); void copySelectedItemsTextToClipboard(); void selectItemIndices(int startIndex, int endIndex); }; } // namespace  minexpert } // namespace MsXpS source/XpertMassGui/includes/MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp000664 001750 001750 00000005325 15100504560 035565 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// Qt includes #include /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { class DECLSPEC ScriptingObjectTreeWidgetItem: public QObject, QTreeWidgetItem { Q_OBJECT friend class JavaScriptingWnd; public: ScriptingObjectTreeWidgetItem(QTreeWidget *parent, int type); ScriptingObjectTreeWidgetItem(QTreeWidgetItem *parent, int type); ~ScriptingObjectTreeWidgetItem(); void setQObject(QObject *object); void setComment(const QString &comment); void setHelp(const QString &help); void setColor(const QColor &color); void setScriptEntityType(ScriptEntityType type); void setProperties(QObject *object = Q_NULLPTR, const QString &comment = QString(), const QString &help = QString(), const QColor &color = Qt::black, ScriptEntityType entityType = ScriptEntityType::UndefinedEntity); public slots: void setText(int column, const QString &text); private: QObject *mp_qobject = Q_NULLPTR; QString m_comment; QString m_help; QColor m_color; ScriptEntityType m_entityType; }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/ToleranceWidget.hpp000664 001750 001750 00000006277 15100504560 033037 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ #pragma once /////////////////////// stdlib includes /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/Tolerance.hpp" /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace Ui { class ToleranceWidget; } namespace MsXpS { namespace libXpertMassGui { class DECLSPEC ToleranceWidget : public QWidget { Q_OBJECT public: ToleranceWidget(QWidget *parent, const QString &config_settings_file_path, double nominal, libXpertMassCore::Tolerance::Type type); ToleranceWidget(QWidget *parent, const QString &config_settings_file_path); ~ToleranceWidget(); void writeSettings(const QString &config_settings_file_path); void readSettings(const QString &config_settings_file_path); Q_INVOKABLE void setNominal(double nominal); Q_INVOKABLE double getNominal() const; Q_INVOKABLE void setType(libXpertMassCore::Tolerance::Type type); Q_INVOKABLE libXpertMassCore::Tolerance::Type getType() const; public slots: void nominalDoubleSpinBoxValueChanged(double value); void typeComboBoxValueChanged(const QString &text); signals: void nominalChangedSignal(); void typeChangedSignal(); void toleranceChangedSignal(std::pair); private: QString m_configSettingsFilePath; bool m_isInitialized = false; libXpertMassCore::Tolerance *mp_tolerance = new libXpertMassCore::Tolerance(this); Ui::ToleranceWidget *mp_ui; void initializeTolerance(double nominal, libXpertMassCore::Tolerance::Type type); void initializeTolerance(const libXpertMassCore::Tolerance &tolerance); bool initializeToleranceFromSettings(); void setupWidgetCommon(); void setupWidget(); void setupWidget(double nominal, libXpertMassCore::Tolerance::Type type); }; } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/includes/MsXpS/libXpertMassGui/XpertMassGuiJavaScript.hpp000664 001750 001750 00000001070 15100504560 034323 0ustar00rusconirusconi000000 000000 #pragma once /////////////////////// stdlib includes /////////////////////// Qt includes #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGui includes /////////////////////// Local includes #include "MsXpS/export-import-config.h" namespace MsXpS { namespace libXpertMassGui { DECLSPEC void registerEnumsToQJSEngine(QJSEngine *engine); DECLSPEC void registerGlobalsToQJSEngine(QJSEngine *engine); } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/000775 001750 001750 00000000000 15100507137 022122 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/src/ColorSelector.cpp000664 001750 001750 00000030164 15100504560 025406 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2019 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassGui/ColorSelector.hpp" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::ColorSelector \inmodule libXpertMassGui \ingroup ColorSelector \inheaderfile ColorSelector.hpp \brief The ColorSelector class provides a pool of colors that are associated to a boolean value that is a flag indicating if the colors are in use or not. Facilities allow for recycling colors when they are not used anymore. */ /*! \typealias MsXpS::libXpertMassGui::ColorUsagePair Synonym for std::pair. The boolean value is a flag that tells if the color is currently in use. */ /*! \variable MsXpS::libXpertMassGui::ColorSelector::m_usageOfColors \brief Pool of available colors associated to their usage status. The pool of colors is in the form of a static vector of \l ColorUsagePair instances. */ // Fill-in the colors that can be used. Set them to unused to start with. using color_usage_pair = std::pair; std::vector ColorSelector::m_usageOfColors = { ColorUsagePair(QColor().fromRgb(0xff0000), false), ColorUsagePair(QColor().fromRgb(0x0000ff), false), ColorUsagePair(QColor().fromRgb(0x2a7622), false), ColorUsagePair(QColor().fromRgb(0xff00ff), false), ColorUsagePair(QColor().fromRgb(0xffa400), false), ColorUsagePair(QColor().fromRgb(0x00ffd7), false), ColorUsagePair(QColor().fromRgb(0x7a00ff), false), ColorUsagePair(QColor().fromRgb(0xff009c), false), ColorUsagePair(QColor().fromRgb(0x0072ff), false), ColorUsagePair(QColor().fromRgb(0xf08080), false), ColorUsagePair(QColor().fromRgb(0x4682b4), false), ColorUsagePair(QColor().fromRgb(0x556b2f), false), ColorUsagePair(QColor().fromRgb(0x7dbee7), false)}; /*! \brief Constructs a ColorSelector instance. */ ColorSelector::ColorSelector() { // List all the available colors. // for(auto &&pair : ColorSelector::m_usageOfColors) // qDebug() << "Mapped color:" << pair.first.rgb(); } /*! \brief Destructs this ColorSelector instance. */ ColorSelector::~ColorSelector() { } /*! \brief Add \a color to the vector of colors. If \a color is not already in the member vector of colors, it is added to it with a usage flag set to false. */ void ColorSelector::addColor(const QColor &color) { if(!color.isValid()) { // qDebug() << "The color to add is invalid."; return; } auto result = std::find_if( m_usageOfColors.begin(), m_usageOfColors.end(), [color](const ColorUsagePair &item) { return item.first == color; }); if(result == m_usageOfColors.end()) { m_usageOfColors.push_back(ColorUsagePair(color, false)); } else { // qDebug() << "The color to be added is already present in the pool." // "Doing nothing."; } } /*! \brief Returns a color. If there is a color in the pool that is not used, it is returned and its use flag is set to true. If there is not a single color in the pool that is available for use, a new color needs to be prepared. Either \a make_random is true and a random color is created, put in the pool with the use flag set to true and returned, or the user is provided with the standard color-choosing dialog. \sa chooseColor() */ QColor ColorSelector::getColor(bool make_random) { // qDebug(); // We are asked to provide a color. Either we have one available, or we need // to provide the user with the opportunity to choose a new one or to // construct a new one randomly. // Search if there is still a color in the pool that is *not* used. bool is_used = false; auto result = std::find_if( m_usageOfColors.begin(), m_usageOfColors.end(), [is_used](const ColorUsagePair &item) { return item.second == is_used; }); QColor color; if(result != m_usageOfColors.end()) { // qDebug() << "The color from the map is fine. Set it to used."; // Something was returned. By necessity it is not used (find_if above). color = result->first; // qDebug() << "Was it used?" << isUsedColor(color); // Now set it as a used color. result->second = true; // qDebug() << "The returned color is:" << color.rgb(); // qDebug() << "And now is it used?" << isUsedColor(color); return color; } else { // No color was available (pair value false), we need to let the user // choose one with the color chooser or make one random color. if(make_random) { while(1) { color = getRandomColor(); // We need to ensure that the random color is not used already. if(!isUsedColor(color)) { // qDebug() //<< "The color randomly generated is fine. Set it to used."; // At this point we will return a new color that we set as // used in the pool map. m_usageOfColors.push_back(ColorUsagePair(color, true)); return color; } else { // Otherwise keep creating random colors. // qDebug() << "The color randomly generated was used already // !"; } } } // At this point we know the caller wants to provide the user the // opportunity to select a color from the QColorDialog window. return chooseColor(); } return QColor(); } /*! \brief Returns a color. The user is provided with the color-choosing dialog. The choosed color is checked for its presence in the pool of colors. If the color is found in the pool of colors and \a allow_used is true, the color is returned. Else, the user is provided a new opportunity to select an unused color. In that case, the new color is added to the pool of colors and the usage flag is set to true. */ QColor ColorSelector::chooseColor(bool allow_used) { // The user wants to actually choose a color directly from the Qt color // selector dialog. QColor color; while(1) { color = QColorDialog::getColor( Qt::white /* initial color of the returned color */, nullptr, "Please select a color for the plot"); if(!color.isValid()) { // The user cancels the operation. // qDebug() << "The color selection was cancelled by the user."; return color; } // Now test if the color that was selected exists already in the // pool of colors. if(!isUsedColor(color)) { // The color was not used, so we will return it but first insert // it in the map a a used color. // qDebug() //<< "The color selected by the user is not used, so use it!"; m_usageOfColors.push_back(ColorUsagePair(color, true)); return color; } else { // The color has been choosen already. Only return if we can choose an // already chosen color. if(allow_used) return color; } // We'll continue forever :-). Until the user either select an // unused color or abandons the process. // qDebug() << "The color selected by the user is already used or " //"is invalid, please choose another one."; } return color; } /*! \brief Returns a new color randomly created. The color is generated from random H,S,V triplets. */ QColor ColorSelector::getRandomColor() { // Generate random H,S,V triplets. // stackoverflow.com/questions/5008804/generating-random-integer-from-a-range std::random_device rd; // only used once to initialise (seed) engine std::mt19937 rng( rd()); // random-number engine used (Mersenne-Twister in this case) std::uniform_int_distribution uni_h(0, 359); // guaranteed unbiased auto random_h = uni_h(rng); std::uniform_int_distribution uni_s(125, 255); auto random_s = uni_s(rng); std::uniform_int_distribution uni_v(125, 255); auto random_v = uni_s(rng); // Now create the color. return QColor::fromHsv(random_h, random_s, random_v); } /*! \brief Returns a color matching. \a color is searched for in the pool of colors. If it is found, it is returned and the usage flag is set to true (it might have been true already). If \a color is not found in the pool, then it is added to the pool, its usage flag is set to true and that same color is returned. */ QColor ColorSelector::getColor(const QColor &color) { // We are asked to provide a specific color. The only unacceptable situation // would be that the color is in the pool and that it is already used. auto result = std::find_if( m_usageOfColors.begin(), m_usageOfColors.end(), [color](const ColorUsagePair &item) { return item.first == color; }); if(result == m_usageOfColors.end()) { // The color is not in the pool, easy, just add it in the used state. m_usageOfColors.push_back(ColorUsagePair(color, true)); return color; } else { // The color is in the pool, check if it is used or not. if(!result->second) { // The color is not used. Set it to the used state. result->second = true; return color; } } // The color is already used, return an invalid color so the caller knows // it. return QColor(); } /*! \brief Releases \a color. \a color is searched for in the pool of colors. If it is not found, \a color is added to the pool with its usage flag set to false. If \a color is found, its usage flag is set to false (it might have been false already). */ void ColorSelector::releaseColor(const QColor &color) { if(!color.isValid()) return; // Confirm that the color was effectively used. auto result = std::find_if( m_usageOfColors.begin(), m_usageOfColors.end(), [color](const ColorUsagePair &item) { return item.first == color; }); if(result == m_usageOfColors.end()) { // qDebug() << "The color to release does not appear to be in the pool." //<< "Setting it to the pool in unused status."; m_usageOfColors.push_back(ColorUsagePair(color, false)); return; } if(result->second == false) { // qDebug() << "The color to release does not appear to be in use."; return; } // The color was found and is used, just set it to unused status. result->second = false; } /*! \brief Returns true if \a color is in the pool of colors and its usage flag is true, otherwise returns false. */ bool ColorSelector::isUsedColor(const QColor &color) { auto result = std::find_if( m_usageOfColors.begin(), m_usageOfColors.end(), [color](const ColorUsagePair &item) { return item.first == color; }); if(result != m_usageOfColors.end()) { return result->second; } return false; } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/DecimalPlacesOptionsDlg.cpp000664 001750 001750 00000015761 15100504560 027326 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassGui/DecimalPlacesOptionsDlg.hpp" #include "ui_DecimalPlacesOptionsDlg.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::DecimalPlacesOptionsDlg \inmodule libXpertMassGui \ingroup XpertMassGuiUtilities \inheaderfile DecimalPlacesOptionsDlg.hpp \brief The DecimalPlacesOptionsDlg class provides a means to configure the number of decimals that numbers should have when printed as text. */ /*! \variable MsXpS::libXpertMassGui::DecimalPlacesOptionsDlg::m_atomDecPlaces \brief The number of digits after the decimal separator symbol ('.') when printing out the mass of atoms or isotopes (default = 10). */ /*! \variable MsXpS::libXpertMassGui::DecimalPlacesOptionsDlg::m_oligomerDecPlaces \brief The number of digits after the decimal separator symbol ('.') when printing out the mass of oligomers (default = 6). */ /*! \variable MsXpS::libXpertMassGui::DecimalPlacesOptionsDlg::m_polymerDecPlaces \brief The number of digits after the decimal separator symbol ('.') when printing out the mass of polymers (default = 3). */ /*! \variable MsXpS::libXpertMassGui::DecimalPlacesOptionsDlg::m_pkaPhPiDecPlaces \brief The number of digits after the decimal separator symbol ('.') when printing out pH, pKa, pI values (default = 3). */ DecimalPlacesOptionsDlg::DecimalPlacesOptionsDlg( QWidget *parent, [[maybe_unused]] const QString &configSettingsFilePath) : QDialog(parent), mp_ui(new Ui::DecimalPlacesOptionsDlg) { mp_ui->setupUi(this); QSettings settings(m_configSettingsFilePath, QSettings::IniFormat); settings.beginGroup("decimal_places_options_dlg"); restoreGeometry(settings.value("geometry").toByteArray()); settings.endGroup(); // Make now the connections that we will rely upon when // reading places settings and set them to the widgets // which will be reported to the member data. connect( mp_ui->atomDecimalPlacesSpinBox, &QSpinBox::valueChanged, [&](int value) { m_atomDecPlaces = value; }); connect(mp_ui->oligomerDecimalPlacesSpinBox, &QSpinBox::valueChanged, [&](int value) { m_oligomerDecPlaces = value; }); connect(mp_ui->polymerDecimalPlacesSpinBox, &QSpinBox::valueChanged, [&](int value) { m_polymerDecPlaces = value; }); connect(mp_ui->pKaPhPiDecimalPlacesSpinBox, &QSpinBox::valueChanged, [&](int value) { m_pkaPhPiDecPlaces = value; }); connect(mp_ui->validatePushButton, &QPushButton::clicked, this, &DecimalPlacesOptionsDlg::validate); settings.beginGroup("decimal_places_options"); updateDecimalPlaces( settings.value("ATOM_DEC_PLACES", libXpertMassCore::ATOM_DEC_PLACES) .toInt(), settings.value("OLIGOMER_DEC_PLACES", libXpertMassCore::OLIGOMER_DEC_PLACES) .toInt(), settings.value("POLYMER_DEC_PLACES", libXpertMassCore::POLYMER_DEC_PLACES) .toInt(), settings .value("PKA_PH_PI_DEC_PLACES", libXpertMassCore::PKA_PH_PI_DEC_PLACES) .toInt()); settings.endGroup(); } DecimalPlacesOptionsDlg::~DecimalPlacesOptionsDlg() { } void DecimalPlacesOptionsDlg::updateDecimalPlaces(int atom, int oligo, int poly, int pka_ph) { // Will automagically update the member data. mp_ui->atomDecimalPlacesSpinBox->setValue(atom); mp_ui->oligomerDecimalPlacesSpinBox->setValue(oligo); mp_ui->polymerDecimalPlacesSpinBox->setValue(poly); mp_ui->pKaPhPiDecimalPlacesSpinBox->setValue(pka_ph); } void DecimalPlacesOptionsDlg::closeEvent(QCloseEvent *event) { Q_UNUSED(event); QSettings settings(m_configSettingsFilePath, QSettings::IniFormat); settings.beginGroup("decimal_places_options_dlg"); settings.setValue("geometry", saveGeometry()); settings.setValue("ATOM_DEC_PLACES", m_atomDecPlaces); settings.setValue("OLIGOMER_DEC_PLACES", m_oligomerDecPlaces); settings.setValue("POLYMER_DEC_PLACES", m_polymerDecPlaces); settings.setValue("PKA_PH_PI_DEC_PLACES", m_pkaPhPiDecPlaces); settings.endGroup(); } void DecimalPlacesOptionsDlg::validate() { libXpertMassCore::ATOM_DEC_PLACES = mp_ui->atomDecimalPlacesSpinBox->value(); libXpertMassCore::OLIGOMER_DEC_PLACES = mp_ui->oligomerDecimalPlacesSpinBox->value(); libXpertMassCore::POLYMER_DEC_PLACES = mp_ui->polymerDecimalPlacesSpinBox->value(); libXpertMassCore::PKA_PH_PI_DEC_PLACES = mp_ui->pKaPhPiDecimalPlacesSpinBox->value(); QSettings settings(m_configSettingsFilePath, QSettings::IniFormat); settings.beginGroup("decimal_places_options"); settings.setValue("ATOM_DEC_PLACES", libXpertMassCore::ATOM_DEC_PLACES); settings.setValue("OLIGOMER_DEC_PLACES", libXpertMassCore::OLIGOMER_DEC_PLACES); settings.setValue("POLYMER_DEC_PLACES", libXpertMassCore::POLYMER_DEC_PLACES); settings.setValue("PKA_PH_PI_DEC_PLACES", libXpertMassCore::PKA_PH_PI_DEC_PLACES); settings.endGroup(); // At this point ask that masses be redisplayed, for the sequence // editor window. emit decimalPlacesOptionsChangedSignal(); } void DecimalPlacesOptionsDlg::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register DecimalPlacesOptionsDlg class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&DecimalPlacesOptionsDlg::staticMetaObject); engine->globalObject().setProperty("DecimalPlacesOptionsDlg", jsMetaObject); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/IsotopicClusterGeneratorDlg.cpp000664 001750 001750 00000170455 15100504560 030270 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include #include /////////////////////// Qt includes #include #include #include #include #include #include #include /////////////////////// IsoSpec #include #include // extern const int elem_table_atomicNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int elem_table_massNo[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const int // elem_table_extraNeutrons[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const bool elem_table_Radioactive[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; // extern const double // elem_table_log_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES]; /////////////////////// PAPPSO includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassCore/IsotopicData.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataBaseHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataLibraryHandler.hpp" #include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataUserConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicDataManualConfigHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" #include "MsXpS/libXpertMassCore/MassDataCborBaseHandler.hpp" #include "MsXpS/libXpertMassGui/IsotopicDataTableViewModel.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterGeneratorDlg.hpp" #include "MsXpS/libXpertMassGui/ColorSelector.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterGeneratorDlg.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterShaperDlg.hpp" #include "ui_IsotopicClusterGeneratorDlg.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile IsotopicClusterGeneratorDlg.hpp \brief The IsotopicClusterGeneratorDlg class provides a graphical user interface for the generation of an isotopic cluster. The isotopic cluster is generated starting from a Formula. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::mp_ui \brief The graphical user interface definition. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_applicationName \brief The name of the application. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_windowDescription \brief The text to be used for the window title. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_formula \brief The formula that is used as the starting point for the calculations. The formula holds the elemental composition of the analyte for which the calculation should be performed. It must account for the chemical agent responsible for its ionization. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::mp_programWindow \brief The main program window. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_normalizeIntensity \brief The intensity value that the most intense peak in the cluster should have. All the other peaks intensities are normalized against this value. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_maxSummedProbability \brief The sum of all the intensities of the cluster peaks should not exceed this value. By default, the value is 0.95. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_charge \brief The charge of the analyte for which the calculations are performed. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_sortType \brief The type of sorting to use to sort the obtained isotopic cluster's peak centroids. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_sortOrder \brief The order for the sorting (if any) of the obtained isotopic cluster's peak centroids. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::msp_isotopicDataLibrary \brief The isotopic data from the IsoSpec library tables. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::msp_isotopicDataUserConfig \brief The isotopic data from the IsoSpec-like library tables as modified by the user. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::msp_isotopicDataUserManualConfig \brief The isotopic data from the user's manual configuration. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::mpa_libraryStaticTableViewModel \brief The table view model that manages the library static IsoSpec standard data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::mpa_userStaticTableViewModel \brief The table view model that manages the library user-modified IsoSpec standard data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_colorByteArray \brief The color that should be used for displaying the mass spectrum when the isotopic cluster is later shaped into a mass spectrum. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterGeneratorDlg::m_clusterGenerator \brief The isotopic cluster generator that will be used for the computations. */ /*! \brief Constructs a IsotopicClusterGeneratorDlg instance. \list \li \a program_window_p: the program's main window. \li \a applicationName: the name of the application, typically massXpert2 or mineXpert2, for example. \li \a description: the string describing what this dialog window is for (used for the window title). \endlist */ IsotopicClusterGeneratorDlg::IsotopicClusterGeneratorDlg( QWidget *program_window_p, const QString &applicationName, const QString &description) : QDialog(program_window_p), mp_ui(new Ui::IsotopicClusterGeneratorDlg), m_applicationName{applicationName}, m_windowDescription(description), mp_programWindow(program_window_p) { if(!program_window_p) qFatal("Programming error."); mp_ui->setupUi(this); // We want to destroy the dialog when it is closed. setAttribute(Qt::WA_DeleteOnClose); // Right on creation, we need to allocate the isotopic data objects. We will // use the proper isotopic data handler depending on the context (library, // user config or manual config). msp_isotopicDataLibrary = std::make_shared(); msp_isotopicDataUserConfig = std::make_shared(); msp_isotopicDataUserManualConfig = std::make_shared(); setupDialog(); // Update the window title because the window title element in mp_ui->might be // either erroneous or empty. setWindowTitle(QString("%1 - %2").arg(applicationName).arg(description)); } /*! \brief Upon closing of the dialog window (unused \a event), writes the settings to the application configuration file. */ void IsotopicClusterGeneratorDlg::closeEvent([[maybe_unused]] QCloseEvent *event) { writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Destructs this IsotopicClusterGeneratorDlg instance. */ IsotopicClusterGeneratorDlg::~IsotopicClusterGeneratorDlg() { writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); delete mp_ui; } /*! \brief Saves the settings of this dialog window to the application configuration file (\a config_settings_file_path). The saved configuration is read from the file to set back the dialog window in the same status. */ void IsotopicClusterGeneratorDlg::writeSettings( const QString &config_settings_file_path) { QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("IsotopicClusterGeneratorDlg"); settings.setValue("geometry", saveGeometry()); settings.setValue("splitter", mp_ui->splitter->saveState()); // Write all the formulas that sit in the combo box settings.beginWriteArray("libraryformulas"); int count = mp_ui->libraryFormulaComboBox->count(); for(int iter = 0; iter < count; ++iter) { settings.setArrayIndex(iter); QString index; index.setNum(iter); settings.setValue(index, mp_ui->libraryFormulaComboBox->itemText(iter)); } settings.endArray(); // Done writing all the formulas. // Write all the formulas that sit in the combo box settings.beginWriteArray("userconfigformulas"); count = mp_ui->userConfigFormulaComboBox->count(); for(int iter = 0; iter < count; ++iter) { settings.setArrayIndex(iter); QString index; index.setNum(iter); settings.setValue(index, mp_ui->userConfigFormulaComboBox->itemText(iter)); } settings.endArray(); // Done writing all the formulas. settings.endGroup(); } /*! \brief Reads the settings of this dialog window from the application configuration file (\a config_settings_file_path). The configuration is read from the file to set back the dialog window in the same status. */ void IsotopicClusterGeneratorDlg::readSettings( const QString &config_settings_file_path) { QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("IsotopicClusterGeneratorDlg"); restoreGeometry(settings.value("geometry").toByteArray()); mp_ui->splitter->restoreState(settings.value("splitter").toByteArray()); int count = settings.beginReadArray("libraryformulas"); for(int iter = 0; iter < count; ++iter) { settings.setArrayIndex(iter); QString index; index.setNum(iter); QString value = settings.value(index).toString(); mp_ui->libraryFormulaComboBox->insertItem(iter, value); } settings.endArray(); count = settings.beginReadArray("userconfigformulas"); for(int iter = 0; iter < count; ++iter) { settings.setArrayIndex(iter); QString index; index.setNum(iter); QString value = settings.value(index).toString(); mp_ui->userConfigFormulaComboBox->insertItem(iter, value); } settings.endArray(); settings.endGroup(); } /*! \brief Sets up the dialog window. Returns true upon success, or false if the IsoSpec-based isotopic data could not be loaded. */ bool IsotopicClusterGeneratorDlg::setupDialog() { mp_ui->ionChargeSpinBox->setRange(1, 10000); mp_ui->ionChargeSpinBox->setValue(1); readSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); ///////////////// Connections for the different contexts ///////////////// /////////////////// Library isotopic data /////////////////// Library isotopic data // There is no load function because that is automatically performed when // setting up the dialog. connect(mp_ui->saveLibraryPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::saveLibrary); connect(mp_ui->runLibraryPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::runLibrary); connect(mp_ui->addLibraryFormulaPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::addLibraryFormula); connect(mp_ui->removeLibraryFormulaPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::removeLibraryFormula); connect(mp_ui->addUserConfigFormulaPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::addUserConfigFormula); connect(mp_ui->removeUserConfigFormulaPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::removeUserConfigFormula); /////////////////// User config isotopic data /////////////////// User config isotopic data connect(mp_ui->loadUserConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::loadUserConfig); connect(mp_ui->saveUserConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::saveUserConfig); connect(mp_ui->runUserConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::runUserConfig); /////////////////// User manual config isotopic data /////////////////// User manual config isotopic data connect(mp_ui->loadUserManualConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::loadUserManualConfig); connect(mp_ui->saveUserManualConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::saveUserManualConfig); connect(mp_ui->runUserManualConfigPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::runUserManualConfig); ////////////////////// Peak shaping features ///////////////////// connect(mp_ui->toIsotopicClusterShaperPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::toIsotopicClusterShaper); /////////////////// Manual config isotopic data helpers //////////////////// connect(mp_ui->addElementPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::addElementGroupBox); // The color button connect(mp_ui->colorSelectorPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::traceColorPushButtonClicked); // We systematically load the library isotopic data using the dedicated // handler. libXpertMassCore::IsotopicDataLibraryHandler isotopic_data_handler( msp_isotopicDataLibrary); qsizetype non_isotope_skipped_items = 0; qsizetype count = isotopic_data_handler.loadData(non_isotope_skipped_items); if(!count) { message("Failed to load IsoSpec entities from the C++ headers", 5000); qDebug() << "Failed to load IsoSpec entities from the C++ headers."; return false; } // qDebug() << "Loaded isotopic data with" << msp_isotopicDataLibrary->size() //<< "isotopes."; // At dialog creation time, we only load the IsoSpec standard static // element/isotope configuration from the library headers. While loading // these data we populate the model and thus the table view. setupIsoSpecStandardStaticTableView(); return true; } /*! \brief Sets up the IsoSpec tables model and table view. */ void IsotopicClusterGeneratorDlg::setupIsoSpecStandardStaticTableView() { // qDebug(); // Now we can link the data to the table view model. mpa_libraryStaticTableViewModel = new IsotopicDataTableViewModel(this, msp_isotopicDataLibrary); mp_ui->libraryStaticIsotopicDataTableView->setModel( mpa_libraryStaticTableViewModel); mp_ui->libraryStaticIsotopicDataTableView->setIsotopicData( msp_isotopicDataLibrary); mp_ui->libraryStaticIsotopicDataTableView->setParent(this); mpa_libraryStaticTableViewModel->setTableView( mp_ui->libraryStaticIsotopicDataTableView); } /*! \brief Sets up the user-configured IsoSpec-like tables model and table view. */ void IsotopicClusterGeneratorDlg::setupIsoSpecStandardUserTableView() { // qDebug(); // Now we can link the data to the table view model. mpa_userStaticTableViewModel = new IsotopicDataTableViewModel(this, msp_isotopicDataUserConfig); mp_ui->userStaticIsotopicDataTableView->setModel( mpa_userStaticTableViewModel); mp_ui->userStaticIsotopicDataTableView->setIsotopicData( msp_isotopicDataUserConfig); mp_ui->userStaticIsotopicDataTableView->setParent(this); mpa_userStaticTableViewModel->setTableView( mp_ui->userStaticIsotopicDataTableView); } /*! \brief Loads the user-configured IsoSpec-like data. Returns true if successful, false otherwise. */ bool IsotopicClusterGeneratorDlg::loadUserConfig() { QString file_name = QFileDialog::getOpenFileName( this, tr("Load User IsoSpec table"), QDir::home().absolutePath()); if(file_name.isEmpty()) { message("The file name to read from is empty!", 5000); return false; } QFile file(file_name); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { message("Failed to open file for reading.", 5000); return false; } libXpertMassCore::IsotopicDataUserConfigHandler isotopic_data_handler( msp_isotopicDataUserConfig); if(!isotopic_data_handler.loadData(file_name)) { message("Failed to load the isotopic data from the file.", 5000); file.close(); return false; } // Also setup the isospec standard *user* table view, even if that the // beginning it is empty. The user might load user tables from file. setupIsoSpecStandardUserTableView(); file.close(); return true; } /*! \brief Returns true if the IsoSpec isotopic data could be written to file, false otherwise. */ bool IsotopicClusterGeneratorDlg::saveLibrary() { QFileDialog fileDlg(this); fileDlg.setFileMode(QFileDialog::AnyFile); fileDlg.setAcceptMode(QFileDialog::AcceptSave); QString file_name = fileDlg.getSaveFileName( this, tr("Save table"), QDir::home().absolutePath()); if(file_name.isEmpty()) { message("Please provide a file name.", 5000); return false; } libXpertMassCore::IsotopicDataLibraryHandler isotopic_data_handler( msp_isotopicDataLibrary); return isotopic_data_handler.writeData(file_name); } /*! \brief Returns true if the IsoSpec-like user-modified isotopic data could be written to file, false otherwise. */ bool IsotopicClusterGeneratorDlg::saveUserConfig() { QFileDialog fileDlg(this); fileDlg.setFileMode(QFileDialog::AnyFile); fileDlg.setAcceptMode(QFileDialog::AcceptSave); QString file_name = fileDlg.getSaveFileName( this, tr("Save table"), QDir::home().absolutePath()); if(file_name.isEmpty()) { message("Please provide a file name.", 5000); return false; } libXpertMassCore::IsotopicDataUserConfigHandler isotopic_data_handler( msp_isotopicDataUserConfig); return isotopic_data_handler.writeData(file_name); } /*! \brief Reacts to a click onto the push button. Allows selecting a color for the mass spectrum trace to be created later on the basis of the isotopic cluster calculated here. */ void IsotopicClusterGeneratorDlg::traceColorPushButtonClicked() { QPushButton *colored_push_button = mp_ui->colorSelectorPushButton; // Allow (true) the user to select a color that has been chosen already. QColor color = ColorSelector::chooseColor(true); if(color.isValid()) { QPalette palette = colored_push_button->palette(); palette.setColor(QPalette::Button, color); colored_push_button->setAutoFillBackground(true); colored_push_button->setPalette(palette); colored_push_button->update(); // Now prepare the color in the form of a QByteArray QDataStream stream(&m_colorByteArray, QIODevice::WriteOnly); stream << color; } } /*! \brief Copies the results of the computation to an \l IsotopicClusterShaperDlg instance. */ void IsotopicClusterGeneratorDlg::toIsotopicClusterShaper() { // The calculation result is set into a vector that might contain more than // one pair (charge,cluster). In the present case, the dialog window allows // for only one elemental composition to perform the calculation, so we just // need to take the first (and in fact there must be only one) charge,cluster // pair from the vector. We get the trace and we sent that to the peak shaper // dialog. We also want to set the color that might have been set by the user // for plotting the trace. const std::vector &isotopic_cluster_charge_pairs = m_clusterGenerator.getIsotopicClusterChargePairs(); if(!isotopic_cluster_charge_pairs.size()) return; // In the dialog window, only one formula is handled at a time. assert(isotopic_cluster_charge_pairs.size() == 1); // Get the isotopic cluster as a Trace out of the pair. pappso::TraceCstSPtr isotopic_cluster_sp = isotopic_cluster_charge_pairs.front().first; // Make sure we pass the ProgramWindow pointer as the parent object of the // PeakShaperDlg, otherwise crash when using ConsoleWnd. IsotopicClusterShaperDlg *isotopic_cluster_shaper_dlg_p = new IsotopicClusterShaperDlg(mp_programWindow, m_applicationName, "Isotopic cluster shaper", isotopic_cluster_sp, m_normalizeIntensity); isotopic_cluster_shaper_dlg_p->setColorByteArray(m_colorByteArray); isotopic_cluster_shaper_dlg_p->setMassSpectrumTitle( mp_ui->massSpectrumTitleLineEdit->text()); // In that shaper dialog, when the computation has finished, the user may ask // to display the spectrum. So the dialog window emits the signal below. We // want to relay that signal to the other listeners, typically the program // window, so that they can take action. If the program window is of // massXpert, they'll have to craft a CBOR-encoded packet and serve it so that // any listening program can display it (typically mineXpert). If that program // window is of mineXpert, then simply display the mass spectrum in the mass // spectra window... connect(isotopic_cluster_shaper_dlg_p, &IsotopicClusterShaperDlg::displayMassSpectrumSignal, [this](const QString &title, const QByteArray &color_byte_array, pappso::TraceCstSPtr trace) { emit displayMassSpectrumSignal(title, color_byte_array, trace); }); isotopic_cluster_shaper_dlg_p->show(); isotopic_cluster_shaper_dlg_p->raise(); isotopic_cluster_shaper_dlg_p->activateWindow(); } /*! \brief Validates the user manual isotopic data configuration using the \a config_handler configuration handler. The manual configuration is performed by entering by hand the chemical elements (as a symbol) and their isotope (mass,prob) pairs. The element is defined using the symbol. The count of the element in the formula is set in a spin box. Validating the manual configuration means iterating in all the widgets that have been created by the user, extracting from them all the isotopic data. Returns the count of elements in the configuration. */ std::size_t IsotopicClusterGeneratorDlg::validateManualConfig( libXpertMassCore::IsotopicDataManualConfigHandler &config_handler) { // Make clean room. msp_isotopicDataUserManualConfig->clear(); // Each element (symbol, count, isotopes) is packed in a group box. First // get the list of all the QGroupBox *elementGroupBox; that are packed in the // mp_ui->scrollAreaWidgetContents std::size_t element_count; QList elementGroupBoxList = mp_ui->scrollAreaWidgetContents->findChildren( "elementGroupBox"); element_count = elementGroupBoxList.size(); // qDebug() << "The number of elements manually configured is:" << // element_count; if(!element_count) { message("There is currently not a single element defined.", 5000); return 0; } // This loop iterates through the element group boxes, each containing all the // other widgets that are required to define: // - the element symbol // - the count of such elements in the final formula // - the isotopes (listing mass/prob for each isotope) // Vector to collect the isotopes for a given element symbol. Will be cleared // at each new element-symbol/isotopes addition to the isotopic data. QList element_isotopes; // This set is to ensure that we do not have twice the same element frame // (that is, with the same symbol). std::set symbol_set; // This loop iterates through all the element frames that contain the element // symbol and count pair. for(std::size_t iter = 0; iter < element_count; ++iter) { // qDebug() << "Iterating in element group box index" << iter; // Now starting a new element group box (symbol, count, istopes). QGroupBox *currentGroupBox = elementGroupBoxList.at(iter); QLineEdit *symbolLineEdit = currentGroupBox->findChild("symbolLineEdit"); if(symbolLineEdit == nullptr) qFatal("Programming error."); QString symbol = symbolLineEdit->text(); // qDebug() << "The current symbol is" << symbol; // Now check if that symbol was encountered already, which would be an // error. auto res = symbol_set.insert(symbol); if(!res.second) { // We did not insert the symbol because one already existed. That is // an error. message(QString("An element by symbol %1 has already been processed: " "not permitted.") .arg(symbol), 5000); return 0; } // Get the atom count (the index of an element in a formula) int count = currentGroupBox->findChild("atomCountSpinBox")->value(); if(!count) { message(QString("The element by symbol %1 has a count of 0: " "not permitted.") .arg(symbol), 5000); return 0; } // Now handle the isotopic mass/abundance specifications. // // The (mass,prob) isotope pairs are packed in individual isotopeFrame // widgets. // Let's iterate in these frame widgets to extract in turn all the // (mass,prob) pairs for the current element. QList isotopeFrameList = currentGroupBox->findChildren("isotopeFrame"); // qDebug() << "The number of isotopes configured for current element is:" //<< isotopeFrameList.size(); if(!isotopeFrameList.size()) { message(QString("The element by symbol %1 has no isotope defined: " "not permitted.") .arg(symbol), 4000); return 0; } // This loop iterates through all the frames that contain isotope // specifications for a given element symbol-count pair. for(int jter = 0; jter < isotopeFrameList.size(); ++jter) { QFrame *currentFrame = isotopeFrameList.at(jter); QDoubleSpinBox *massSpinBox = currentFrame->findChild( "isotopeMassDoubleSpinBox"); if(massSpinBox == nullptr) qFatal("Programming error."); double mass = massSpinBox->value(); QDoubleSpinBox *probSpinBox = currentFrame->findChild( "isotopeProbDoubleSpinBox"); if(probSpinBox == nullptr) qFatal("Programming error."); double prob = probSpinBox->value(); if(!prob) { message( QString( "The element by symbol %1 has a naught-probability isotope: " "not permitted.") .arg(symbol), 5000); return 0; } // qDebug() << "Iterated in isotope:" << mass << "/" << prob; // At this point create a brand new Isotope with the relevant data. // Isotope::Isotope(int id, // QString element, // QString symbol, // int atomicNo, // double mass, // int massNo, // int extraNeutrons, // double probability, // double logProbability, // bool radioactive) // There are a number of fields that are left to value 0 but this is // of no worries. Store the isotope in the vector of isotopes that // will be added to the isotopic data later when finishing the parsing // of the element frame widget. element_isotopes.push_back( QSharedPointer::create( symbol, symbol, mass, prob)); } // End of iterating in the isotope frame list for a given element symbol. // At this point that we know that for symbol the isotopes were all // correct, we can record all that information to the isotopic data via // the handler. Note that the handler will also record the // element-symbol-count pair for later use. config_handler.newChemicalSet(symbol, count, element_isotopes); // At this point we can reset variables for a new loop iteration. element_isotopes.clear(); symbol = ""; } // End of iterating in the element group box list. // Sanity check: there must be in the symbol/count map member of the isotopic // handler as many different symbols as we had element frames during manual // config data validation. if(element_count != config_handler.getSymbolCountMap().size()) qFatal( "Programming error. The number of stored symbols does not match the " "user's data."); // At this point, make sure we update the maps in the IsotopicData. msp_isotopicDataUserManualConfig->updateMassMaps(); return element_count; } /*! \brief Adds a frame for the definition of an isotope. Returns a pair of spin box widgets, the first for the isotope mass and the second for the probability (abundance). */ std::pair IsotopicClusterGeneratorDlg::addIsotopeFrame() { // qDebug(); // We need to get the QPushButton object that sent the signal. QPushButton *sender = static_cast(QObject::sender()); // qDebug() "sender:" << sender; // The + button was created with the isotopeFrame as its parent. QFrame *isotopeFrame = static_cast(sender->parent()); // The parent of the isotope frame is the elementGroupBox QGroupBox *elementGroupBox = static_cast(isotopeFrame->parent()); // Now get the elementGroupBox's layout where we'll pack the new // isotopeFrame. QVBoxLayout *elementGroupVBoxLayout = static_cast(elementGroupBox->layout()); // Now create the new isotope frame. QFrame *newIsotopeFrame = new QFrame(elementGroupBox); newIsotopeFrame->setObjectName(QStringLiteral("isotopeFrame")); newIsotopeFrame->setFrameShape(QFrame::NoFrame); newIsotopeFrame->setFrameShadow(QFrame::Plain); QGridLayout *newIsotopeFrameGridLayout = new QGridLayout(newIsotopeFrame); newIsotopeFrameGridLayout->setObjectName( QStringLiteral("isotopeFrameGridLayout")); QDoubleSpinBox *newIsotopeMassDoubleSpinBox = new QDoubleSpinBox(newIsotopeFrame); newIsotopeMassDoubleSpinBox->setObjectName( QStringLiteral("isotopeMassDoubleSpinBox")); // if(mass != nullptr) // newIsotopeMassDoubleSpinBox->setValue(*mass); newIsotopeMassDoubleSpinBox->setDecimals(60); newIsotopeMassDoubleSpinBox->setMinimum(0); newIsotopeMassDoubleSpinBox->setMaximum(1000); newIsotopeMassDoubleSpinBox->setToolTip( "Enter the mass of the isotope (like 12.0000 for 12[C])"); newIsotopeFrameGridLayout->addWidget(newIsotopeMassDoubleSpinBox, 0, 0, 1, 1); QDoubleSpinBox *newIsotopeProbDoubleSpinBox = new QDoubleSpinBox(newIsotopeFrame); newIsotopeProbDoubleSpinBox->setObjectName( QStringLiteral("isotopeProbDoubleSpinBox")); // if(prob != nullptr) // newIsotopeProbDoubleSpinBox->setValue(*mass); newIsotopeProbDoubleSpinBox->setDecimals(60); newIsotopeProbDoubleSpinBox->setMinimum(0); newIsotopeProbDoubleSpinBox->setMaximum(1); newIsotopeProbDoubleSpinBox->setToolTip( "Enter the abundance of the isotope (that is, a probability <= 1)"); newIsotopeFrameGridLayout->addWidget(newIsotopeProbDoubleSpinBox, 0, 1, 1, 1); QPushButton *addIsotopePushButton = new QPushButton(newIsotopeFrame); addIsotopePushButton->setObjectName(QStringLiteral("addIsotopePushButton")); connect(addIsotopePushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::addIsotopeFrame); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth( addIsotopePushButton->sizePolicy().hasHeightForWidth()); addIsotopePushButton->setSizePolicy(sizePolicy); addIsotopePushButton->setText("Add isotope"); newIsotopeFrameGridLayout->addWidget(addIsotopePushButton, 0, 2, 1, 1); QPushButton *removeIsotopePushButton = new QPushButton(isotopeFrame); removeIsotopePushButton->setObjectName( QStringLiteral("removeIsotopePushButton")); connect(removeIsotopePushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::removeIsotopeFrame); sizePolicy.setHeightForWidth( removeIsotopePushButton->sizePolicy().hasHeightForWidth()); removeIsotopePushButton->setSizePolicy(sizePolicy); removeIsotopePushButton->setText("Remove isotope"); newIsotopeFrameGridLayout->addWidget(removeIsotopePushButton, 0, 3, 1, 1); elementGroupVBoxLayout->addWidget(newIsotopeFrame); return std::pair( newIsotopeMassDoubleSpinBox, newIsotopeProbDoubleSpinBox); } /*! \brief Removes an isotope frame. The isotope frame to be removed is determined from the identity of the push button that triggered this function. */ void IsotopicClusterGeneratorDlg::removeIsotopeFrame() { // qDebug(); // We need to get the QPushButton object that sent the signal. QPushButton *sender = static_cast(QObject::sender()); // qDebug() "sender:" << sender; // The - button was created with the isotopeFrame as its parent. QFrame *isotopeFrame = static_cast(sender->parent()); // The isotope frame was created with the element group box as its parent. QGroupBox *elementGroupBox = static_cast(isotopeFrame->parent()); QList childrenList = elementGroupBox->findChildren("isotopeFrame"); // We do not want to remove *all* the isotope frames, one must always be // there, // otherwise the element group box is not more usable. if(childrenList.size() < 2) { message("Cannot remove last isotope widget set."); return; } delete isotopeFrame; } /*! \brief Creates a group box that will enshrine a chemical element definition. The group box is populated with a line edit widget for the element symbol and with a spin box for the count of the atoms in the final elemental composition defined here. The isotope-related widgets are not created. \sa addElementGroupBox() */ QGroupBox * IsotopicClusterGeneratorDlg::addElementSkeletonGroupBox() { QGroupBox *elementGroupBox; elementGroupBox = new QGroupBox(mp_ui->scrollAreaWidgetContents); elementGroupBox->setObjectName(QStringLiteral("elementGroupBox")); QVBoxLayout *elementGroupVBoxLayout; elementGroupVBoxLayout = new QVBoxLayout(elementGroupBox); elementGroupVBoxLayout->setObjectName( QStringLiteral("elementGroupVBoxLayout")); QHBoxLayout *symbolLineEditHBoxLayout = new QHBoxLayout(); QLineEdit *symbolLineEdit; symbolLineEdit = new QLineEdit(elementGroupBox); symbolLineEdit->setObjectName(QStringLiteral("symbolLineEdit")); symbolLineEdit->setPlaceholderText("Symbol"); symbolLineEdit->setToolTip("Enter the symbol of the chemical element"); symbolLineEditHBoxLayout->addWidget(symbolLineEdit); QSpinBox *atomCountSpinBox; atomCountSpinBox = new QSpinBox(elementGroupBox); atomCountSpinBox->setObjectName(QStringLiteral("atomCountSpinBox")); atomCountSpinBox->setRange(1, 100000000); atomCountSpinBox->setToolTip( "Enter the count of the chemical element in the formula"); symbolLineEditHBoxLayout->addWidget(atomCountSpinBox); QSpacerItem *symbolLineEditHBoxLayoutHorizontalSpacer; symbolLineEditHBoxLayoutHorizontalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); symbolLineEditHBoxLayout->addItem(symbolLineEditHBoxLayoutHorizontalSpacer); QPushButton *minusElementPushButton; minusElementPushButton = new QPushButton(elementGroupBox); minusElementPushButton->setObjectName( QStringLiteral("minusElementPushButton")); connect(minusElementPushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::removeElementGroupBox); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth( minusElementPushButton->sizePolicy().hasHeightForWidth()); minusElementPushButton->setSizePolicy(sizePolicy); minusElementPushButton->setText("Remove element"); symbolLineEditHBoxLayout->addWidget(minusElementPushButton); elementGroupVBoxLayout->addLayout(symbolLineEditHBoxLayout); // Setting SetFixedSize helps maintaining the widgets most compact without // having to resort to vertical spacers. mp_ui->scrollAreaWidgetContentsVerticalLayout->setSizeConstraint( QLayout::SetFixedSize); mp_ui->scrollAreaWidgetContentsVerticalLayout->addWidget(elementGroupBox); return elementGroupBox; } /*! \brief Returns a newly created frame inside \a elementGroupBox that will enshrine all the specifications about the isotopes being configured. */ QFrame * IsotopicClusterGeneratorDlg::createIsotopeFrame(QGroupBox *elementGroupBox) { QFrame *isotopeFrame; // If elementGroupBox is nullptr, the frame is not parented. It is then up // to // the caller to parent it. isotopeFrame = new QFrame(elementGroupBox); isotopeFrame->setObjectName(QStringLiteral("isotopeFrame")); isotopeFrame->setFrameShape(QFrame::NoFrame); isotopeFrame->setFrameShadow(QFrame::Plain); QGridLayout *isotopeFrameGridLayout; isotopeFrameGridLayout = new QGridLayout(isotopeFrame); isotopeFrameGridLayout->setObjectName( QStringLiteral("isotopeFrameGridLayout")); QDoubleSpinBox *isotopeMassDoubleSpinBox; isotopeMassDoubleSpinBox = new QDoubleSpinBox(isotopeFrame); isotopeMassDoubleSpinBox->setObjectName( QStringLiteral("isotopeMassDoubleSpinBox")); isotopeMassDoubleSpinBox->setDecimals(60); isotopeMassDoubleSpinBox->setMinimum(0); isotopeMassDoubleSpinBox->setMaximum(1000); isotopeMassDoubleSpinBox->setToolTip( "Enter the mass of the isotope (like 12.0000 for 12[C])"); isotopeFrameGridLayout->addWidget(isotopeMassDoubleSpinBox, 0, 0, 1, 1); QDoubleSpinBox *isotopeProbDoubleSpinBox; isotopeProbDoubleSpinBox = new QDoubleSpinBox(isotopeFrame); isotopeProbDoubleSpinBox->setObjectName( QStringLiteral("isotopeProbDoubleSpinBox")); isotopeProbDoubleSpinBox->setDecimals(60); isotopeProbDoubleSpinBox->setMinimum(0); isotopeProbDoubleSpinBox->setMaximum(1); isotopeProbDoubleSpinBox->setToolTip( "Enter the abundance of the isotope (that is, a probability <= 1)"); isotopeFrameGridLayout->addWidget(isotopeProbDoubleSpinBox, 0, 1, 1, 1); QPushButton *addIsotopePushButton; addIsotopePushButton = new QPushButton(isotopeFrame); addIsotopePushButton->setObjectName(QStringLiteral("addIsotopePushButton")); connect(addIsotopePushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::addIsotopeFrame); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth( addIsotopePushButton->sizePolicy().hasHeightForWidth()); addIsotopePushButton->setSizePolicy(sizePolicy); addIsotopePushButton->setText("Add isotope"); isotopeFrameGridLayout->addWidget(addIsotopePushButton, 0, 2, 1, 1); QPushButton *removeIsotopePushButton; removeIsotopePushButton = new QPushButton(isotopeFrame); removeIsotopePushButton->setObjectName( QStringLiteral("removeIsotopePushButton")); connect(removeIsotopePushButton, &QPushButton::clicked, this, &IsotopicClusterGeneratorDlg::removeIsotopeFrame); sizePolicy.setHeightForWidth( removeIsotopePushButton->sizePolicy().hasHeightForWidth()); removeIsotopePushButton->setSizePolicy(sizePolicy); removeIsotopePushButton->setText("Remove isotope"); isotopeFrameGridLayout->addWidget(removeIsotopePushButton, 0, 3, 1, 1); QVBoxLayout *elementGroupVBoxLayout = static_cast(elementGroupBox->layout()); elementGroupVBoxLayout->addWidget(isotopeFrame); return isotopeFrame; } /*! \brief Creates a group box that will enshrine a chemical element definition. The group box is populated with a line edit widget for the element symbol and with a spin box for the count of the atoms in the final elemental composition defined here. The isotope-related frame is then created inside the newly created group box. \sa addElementSkeletonGroupBox() */ QGroupBox * IsotopicClusterGeneratorDlg::addElementGroupBox() { QGroupBox *elementGroupBox = addElementSkeletonGroupBox(); // And now add the first isotope frame createIsotopeFrame(elementGroupBox); return elementGroupBox; } /*! \brief Removes an element group box. The group box to be removed is determined from the identity of the push button that triggered this function. */ void IsotopicClusterGeneratorDlg::removeElementGroupBox() { // qDebug(); // We need to get the QPushButton object that sent the signal. QPushButton *sender = static_cast(QObject::sender()); // qDebug() "sender:" << sender; // The - button was created with the element group box as its parent. QGroupBox *elementGroupBox = static_cast(sender->parent()); delete elementGroupBox; } /*! \brief Prints \a message in the message line edit widget, that will be erased after the \a timeout. */ void IsotopicClusterGeneratorDlg::message(const QString &message, int timeout) { mp_ui->messageLineEdit->setText(message); QTimer::singleShot(timeout, [this]() { mp_ui->messageLineEdit->setText(""); }); } /*! \brief Returns true if all the parameters are correct and consistent, false otherwise. */ bool IsotopicClusterGeneratorDlg::checkParameters() { // We need to check a number of parameters before running the computation. // Let's get the summed probs value configure by the user m_maxSummedProbability = mp_ui->probabilityDoubleSpinBox->value(); m_charge = mp_ui->ionChargeSpinBox->value(); if(!m_charge) qFatal("The charge cannot be 0 here."); double gaussian_apex_intensity = 0; if(mp_ui->intensityGroupBox->isChecked()) { bool ok = false; gaussian_apex_intensity = mp_ui->gaussianIntensityValueLineEdit->text().toDouble(&ok); if(!ok) { message( "The Gaussian apex intensity value failed to convert to double. " "Please, fix it.", 5000); return false; } if(gaussian_apex_intensity < 0) { message( "The Gaussian apex intensity value is negative, converted to " "positive. ", 5000); gaussian_apex_intensity = -gaussian_apex_intensity; } if(!gaussian_apex_intensity) { message( "The Gaussian apex intensity value is naught. Please, fix " "it.", 5000); return false; } // qDebug() << "gaussian_apex_intensity:" << gaussian_apex_intensity; // Now that we know that the value is reliable, we can set it to the // member datum. m_normalizeIntensity = gaussian_apex_intensity; // qDebug() << "Should perform normalization."; } else { m_normalizeIntensity = std::numeric_limits::min(); } bool should_sort = !mp_ui->noSortRadioButton->isChecked(); if(!should_sort) m_sortType = pappso::Enums::SortType::none; else { if(mp_ui->sortByMzRadioButton->isChecked()) m_sortType = pappso::Enums::SortType::x; else m_sortType = pappso::Enums::SortType::y; // At this point the sort order. if(mp_ui->ascendingSortRadioButton->isChecked()) m_sortOrder = pappso::Enums::SortOrder::ascending; else m_sortOrder = pappso::Enums::SortOrder::descending; } return true; } /*! \brief Runs the computation using the IsoSpec-based isotopic data. Returns true upon success, false otherwise. */ bool IsotopicClusterGeneratorDlg::runLibrary() { // qDebug(); // This is the configuration of elements/isotopes that is loaded from the // IsoSpec library headers, that is, the configuration is static. The isotopic // data were loaded at setupDialog() time. assert(msp_isotopicDataLibrary->size()); if(!checkParameters()) { message("The parameters failed to check.", 6000); return false; } // Get the formula for which the isotopic computation is to be performed. QString formula_text = mp_ui->libraryFormulaComboBox->currentText(); if(formula_text.isEmpty()) { message( "The formula cannot be empty. Make sure it has the H2O1 syntax (note " "the 1 index for element O", 5000); return false; } // Now configure the generator. m_clusterGenerator.setIsotopicData(msp_isotopicDataLibrary); m_clusterGenerator.setIsotopicDataType( libXpertMassCore::IsotopicDataType::LIBRARY_CONFIG); // Create the pair that we'll feed to the generator. std::pair formula_charge_pair(formula_text, m_charge); m_clusterGenerator.setFormulaChargePair(formula_charge_pair); m_clusterGenerator.setMaxSummedProbability(m_maxSummedProbability); m_clusterGenerator.setNormalizationIntensity(m_normalizeIntensity); m_clusterGenerator.setSortType(m_sortType); m_clusterGenerator.setSortOrder(m_sortOrder); if(m_clusterGenerator.run()) { reportResults(); return true; } else message("Failed to perform the computation.", 6000); return false; } /*! \brief Runs the computation using the IsoSpec-like user-modified isotopic data. Returns true upon success, false otherwise. */ bool IsotopicClusterGeneratorDlg::runUserConfig() { // qDebug(); // When running this function, the user must have a correct atom/isotope // configuration in the tableview and a formula that matches the available // chemical elements. // The correct isotopic data handler is used to perform the preparation of the // arrays needed by IsoSpec. if(!msp_isotopicDataUserConfig->size()) { QMessageBox::warning( this, QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription), "Please, load a user-defined isotopic data table.\n" "Alternatively, use the static data table view (other tab)\n" "and modify it in place, then save it to disk.\n" "Finally load these isotopic data from file into this table view.", QMessageBox::Ok); return false; } if(!checkParameters()) { message("The parameters failed to check.", 6000); return false; } // Get the formula for which the isotopic computation is to be performed. QString formula_text = mp_ui->userConfigFormulaComboBox->currentText(); if(formula_text.isEmpty()) { message( "The formula cannot be empty. Make sure it has the H2O1 syntax (note " "the 1 index for element O", 5000); return false; } // Now configure the generator. m_clusterGenerator.setIsotopicData(msp_isotopicDataUserConfig); m_clusterGenerator.setIsotopicDataType( libXpertMassCore::IsotopicDataType::USER_CONFIG); // Create the pair that we'll feed to the m_clusterGenerator. std::pair formula_charge_pair(formula_text, m_charge); m_clusterGenerator.setFormulaChargePair(formula_charge_pair); m_clusterGenerator.setMaxSummedProbability(m_maxSummedProbability); m_clusterGenerator.setNormalizationIntensity(m_normalizeIntensity); m_clusterGenerator.setSortType(m_sortType); m_clusterGenerator.setSortOrder(m_sortOrder); if(m_clusterGenerator.run()) { reportResults(); return true; } else message("Failed to perform the computation.", 6000); return false; } /*! \brief Runs the computation using the user manually-configured isotopic data. Returns true upon success, false otherwise. */ bool IsotopicClusterGeneratorDlg::runUserManualConfig() { // qDebug(); // First validate the configuration. Errors are fatal in the IsoSpec // library. In order to validate the configuration and keep interesting data // in it, we first instantiate a specific handler that we'll pass to the // validation function. // Make clean room. msp_isotopicDataUserManualConfig->clear(); // Here, we do not have a formula to make the computation with because the // user has manually configured the isotopes and for each symbol as also told // how many such atoms are the the compound. The determination of the // symbol/count pairs is done in the validateManualConfig() call below. libXpertMassCore::IsotopicDataManualConfigHandler isotopic_data_handler( msp_isotopicDataUserManualConfig); std::size_t element_count = validateManualConfig(isotopic_data_handler); if(!element_count) { // No messages because the validation function does the job. return false; } QString formula_text = isotopic_data_handler.craftFormula(); assert(msp_isotopicDataUserManualConfig->size()); if(!checkParameters()) { message("The parameters failed to check.", 6000); return false; } // Now configure the generator. m_clusterGenerator.setIsotopicData(msp_isotopicDataUserManualConfig); m_clusterGenerator.setIsotopicDataType( libXpertMassCore::IsotopicDataType::MANUAL_CONFIG); // Create the pair that we'll feed to the m_clusterGenerator. std::pair formula_charge_pair(formula_text, m_charge); m_clusterGenerator.setFormulaChargePair(formula_charge_pair); m_clusterGenerator.setMaxSummedProbability(m_maxSummedProbability); m_clusterGenerator.setNormalizationIntensity(m_normalizeIntensity); m_clusterGenerator.setSortType(m_sortType); m_clusterGenerator.setSortOrder(m_sortOrder); if(m_clusterGenerator.run()) { reportResults(); return true; } else message("Failed to perform the computation.", 6000); return false; } /*! \brief Loads the user manually-configured isotopic data configuration. All the widgets required to actually display the user manual configuration are created and configured automatically. Returns true upon success, false otherwise. */ bool IsotopicClusterGeneratorDlg::loadUserManualConfig() { // File format: // //[Element] // symbol C count 100 //[Isotopes] 2 // mass 12.000 prob 0.9899 // mass 13.000 prob 0.010 QString file_name = QFileDialog::getOpenFileName( this, tr("Load configuration"), QDir::home().absolutePath()); if(file_name.isEmpty()) { message("The file name to read from is empty!", 5000); return false; } QFile file(file_name); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { message("Failed to open file for reading.", 5000); return false; } // Make sure we clear the room. msp_isotopicDataUserManualConfig->clear(); // Instantiate the proper handler! libXpertMassCore::IsotopicDataManualConfigHandler isotopic_data_handler( msp_isotopicDataUserManualConfig); qsizetype isotope_count = isotopic_data_handler.loadData(file_name); if(!isotope_count) { qDebug() << "Failed to load any isotope from the configuration file."; return false; } // At this point, we have all the required data to start creating the // widgets! initializeUserManualConfigurationWidgets(isotopic_data_handler); return true; } /*! \brief Saves the user manually-entered isotopic data configuration to file. The user is provided with a file selection dialog window. Returns true upon succes, false if the manual isotopic data configuration does not validate successfully. */ bool IsotopicClusterGeneratorDlg::saveUserManualConfig() { // We need to iterate into the various widgets, exactly as done for the // run of the manual configuration. // Make clean room. msp_isotopicDataUserManualConfig->clear(); // Here, we do not have a formula to make the computation with because the // user has manually configured the isotopes and for each symbol as also told // how many such atoms are the the compound. The determination of the // symbol/count pairs is done in the validateManualConfig() call below. libXpertMassCore::IsotopicDataManualConfigHandler isotopic_data_handler( msp_isotopicDataUserManualConfig); std::size_t element_count = validateManualConfig(isotopic_data_handler); if(!element_count) { // No messages because the validation function does the job. return false; } // At this point all the isotopic data have been parsed and are available for // writing. We ask the isotopic data handler to write to the file. // At this point let the user choose a file for that config. QFileDialog fileDlg(this); fileDlg.setFileMode(QFileDialog::AnyFile); fileDlg.setAcceptMode(QFileDialog::AcceptSave); QString file_name = fileDlg.getSaveFileName( this, tr("Save configuration"), QDir::home().absolutePath()); if(file_name.isEmpty()) { message("Please provide a file name.", 5000); return false; } isotopic_data_handler.writeData(file_name); message(QString("Configuration saved in %1").arg(file_name), 5000); return true; } /*! \brief Initializes all the widgets required to host the user manually-configured isotopic data. This function should be called right after having loaded a user manually-defined isotopic data configuration. \a config_handler is the isotopic data configuration handler that had previously loaded the data. Returns true upon success, false if the user manual configuration is empty. \sa loadUserManualConfig() */ bool IsotopicClusterGeneratorDlg::initializeUserManualConfigurationWidgets( const libXpertMassCore::IsotopicDataManualConfigHandler &config_handler) { // This function must be called right after having loaded a manual // configuration file because during that file reading, all the atomistic info // have been set and the handler holds some more of use. if(!msp_isotopicDataUserManualConfig->size()) { qDebug() << "There are no data to display in the widgets."; return false; } // Now iterate in all the data and create widgets to display them. using SymbolCountMap = std::map; const SymbolCountMap &map = config_handler.getSymbolCountMap(); // qDebug() << "The symbol/count map has this count of items:" << map.size(); // Get all the unqiue isotope symbols that were set the isotopic data object. // Remember that in the manual config context, only isotopes from the user's // manual configuration have been set in the IsotopicData object (which is not // true for library/user isotopic data. std::vector symbols = msp_isotopicDataUserManualConfig->getUniqueSymbolsInOriginalOrder(); // We can now iterate in each symbol and start getting the data. for(auto symbol : symbols) { // This call throws if the symbol is not there. int symbol_count = map.at(symbol); // qDebug() << "For symbol" << symbol << "the count is:" << symbol_count; // This is where we contruct a new elementGroupBox QGroupBox *elementGroupBox = addElementSkeletonGroupBox(); // Now the symbol line edit widget QLineEdit *symbolLineEdit = elementGroupBox->findChild("symbolLineEdit"); symbolLineEdit->setText(symbol); // Now the symbol count spin box widget QSpinBox *atomCountSpinBox = elementGroupBox->findChild("atomCountSpinBox"); atomCountSpinBox->setValue(symbol_count); // Now get iterators to the range of isotopes by that symbol in the // isotopic data. libXpertMassCore::IsotopeListCstIteratorPair iter_pair = msp_isotopicDataUserManualConfig->getIsotopesBySymbol(symbol); // We can now iterate in the range and extract the isotopes. while(iter_pair.first != iter_pair.second) { libXpertMassCore::IsotopeQSPtr isotope_qsp = *iter_pair.first; // The frame is automatically parented to the skeleton // elementGroupBox that we created above. QFrame *isotopeFrame = createIsotopeFrame(elementGroupBox); QDoubleSpinBox *massSpinBox = isotopeFrame->findChild( "isotopeMassDoubleSpinBox"); massSpinBox->setValue(isotope_qsp->getMass()); QDoubleSpinBox *probSpinBox = isotopeFrame->findChild( "isotopeProbDoubleSpinBox"); probSpinBox->setValue(isotope_qsp->getProbability()); ++iter_pair.first; } } // End // iterating in the unique symbols in their original order. return true; } /*! \brief Adds the currently displayed formula for the IsoSpec-based table view to the corresponding combo box widget. */ void IsotopicClusterGeneratorDlg::addLibraryFormula() { QString text = mp_ui->libraryFormulaComboBox->currentText(); if(text.isEmpty()) return; mp_ui->libraryFormulaComboBox->addItem(text); } /*! \brief Removes the currently displayed formula for the IsoSpec-based table view from the corresponding combo box widget. */ void IsotopicClusterGeneratorDlg::removeLibraryFormula() { int currentIndex = mp_ui->libraryFormulaComboBox->currentIndex(); if(currentIndex != -1) mp_ui->libraryFormulaComboBox->removeItem(currentIndex); } /*! \brief Adds the currently displayed formula for the IsoSpec-like user-modified table view to the corresponding combo box widget. */ void IsotopicClusterGeneratorDlg::addUserConfigFormula() { QString text = mp_ui->userConfigFormulaComboBox->currentText(); if(text.isEmpty()) return; mp_ui->userConfigFormulaComboBox->addItem(text); } /*! \brief Removes the currently displayed formula for the IsoSpec-like user-modified table view from the corresponding combo box widget. */ void IsotopicClusterGeneratorDlg::removeUserConfigFormula() { int currentIndex = mp_ui->userConfigFormulaComboBox->currentIndex(); if(currentIndex != -1) mp_ui->userConfigFormulaComboBox->removeItem(currentIndex); } /*! \brief Exports the computation results to a string that is set to the plain text edit widget in the results tab widget tab. */ void IsotopicClusterGeneratorDlg::reportResults() { // Finally export the results as a string. // Note how we do export the relative intensity. If there was normalization, // that value was updated, otherwise it had been initialized identical to // the intensity upon creation of the PeakCentroid instances in the // vector. libXpertMassCore::IsotopicClusterChargePair isotopic_cluster_charge_pair = m_clusterGenerator.getIsotopicClusterChargePairs().front(); QString results = m_clusterGenerator.clustersToString(); // qDebug().noquote() << results; mp_ui->isoSpecTabWidget->setCurrentWidget(mp_ui->isoSpecTabWidgetResultsTab); mp_ui->isoSpecOutputDataPlainTextEdit->setPlainText(results); } } // namespace libXpertMassGui } // namespace MsXpS #if 0 Example from IsoSpec. const int elementNumber = 2; const int isotopeNumbers[2] = {2,3}; const int atomCounts[2] = {2,1}; const double hydrogen_masses[2] = {1.00782503207, 2.0141017778}; const double oxygen_masses[3] = {15.99491461956, 16.99913170, 17.9991610}; const double* isotope_masses[2] = {hydrogen_masses, oxygen_masses}; const double hydrogen_probs[2] = {0.5, 0.5}; const double oxygen_probs[3] = {0.5, 0.3, 0.2}; const double* probs[2] = {hydrogen_probs, oxygen_probs}; IsoLayeredGenerator iso(Iso(elementNumber, isotopeNumbers, atomCounts, isotope_masses, probs), 0.99); #endif libxpertmass-1.4.0/source/XpertMassGui/src/IsotopicClusterShaperDlg.cpp000664 001750 001750 00000101612 15100504560 027551 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include // for std::numeric_limits /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes #include #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp" #include "MsXpS/libXpertMassCore/IsotopicClusterGenerator.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaper.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterShaperDlg.hpp" #include "MsXpS/libXpertMassGui/ColorSelector.hpp" #include "ui_IsotopicClusterShaperDlg.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::IsotopicClusterShaperDlg \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile IsotopicClusterShaperDlg.hpp \brief The IsotopicClusterShaperDlg class provides a graphical user interface for the full peak shaping process. Each peak centroid in the isotopic cluster is shaped independently and all the obtained shapes are combined into a single mass spectrum. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::mp_ui \brief The graphical user interface definition. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::mp_massPeakShaperConfigWidget \brief The widget where the mass peak shaping process configuration is done.. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::mp_programWindow \brief The main program window. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_applicationName \brief The name of the application. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_description \brief The description of this IsotopicClusterShaperDlg dialog window instance. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_fileName \brief The name of the file to which results might be written. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_mapTrace \brief The map trace that is used to combine each individual peak shape into a single mass spectrum. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_finalTrace \brief The finale pappso::Trace in which to store the final mass spectrum. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_colorByteArray \brief The byte array representation of the color with which the spectrum might be traced. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_mzIntegrationParams \brief The mass spectrum combination parameters (size of the bins, for example). */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_referencePeakMz \brief The m/z value that is used as the reference peak when convertring full with at half maximum values to and from resolving power values. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_normalizingIntensity \brief The intensity value that the most intense peak in the cluster should have. All the other peaks intensities are normalized against this value. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_isotopicCluster \brief The isotopic cluster in the form of a pappso::Trace instance. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_peakShapers \brief The list of peak shapers used to shape the peak around each isotopic cluster peak centroid. Each peak centroid of the isotopic cluster will be handled by its own peak shaper instance. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::m_config \brief The peak shaping process configuration. */ /*! \variable MsXpS::libXpertMassGui::IsotopicClusterShaperDlg::msp_msgTimer \brief The timer used to erase messages output to the user. */ /*! \brief Constructs a IsotopicClusterShaperDlg instance. \list \li \a program_window_p: the program's main window. \li \a applicationName: the name of the application, typically massXpert2 or mineXpert2, for example. \li \a description: the string describing what this dialog window is for (used for the window title). \endlist */ IsotopicClusterShaperDlg::IsotopicClusterShaperDlg( QWidget *program_window_p, const QString &applicationName, const QString &description) : QDialog(static_cast(program_window_p)), mp_ui(new Ui::IsotopicClusterShaperDlg), mp_programWindow(program_window_p), m_applicationName(applicationName), m_description(description) { if(!program_window_p) qFatal("Programming error. Program aborted."); setupDialog(); // Update the window title because the window title element in m_ui might be // either erroneous or empty. setWindowTitle(QString("%1 - %2").arg(applicationName).arg(description)); } /*! \brief Constructs a IsotopicClusterShaperDlg instance. \list \li \a program_window_p: the program's main window. \li \a applicationName: the name of the application, typically massXpert2 or mineXpert2, for example. \li \a description: the string describing what this dialog window is for (used for the window title). \li \a isotopic_cluster_sp: the isotopic cluster that is to be processed in order to shape each one of its peak centroids. \endlist */ IsotopicClusterShaperDlg::IsotopicClusterShaperDlg( QWidget *program_window_p, const QString &applicationName, const QString &description, pappso::TraceCstSPtr isotopic_cluster_sp, int normalizing_intensity) : QDialog(static_cast(program_window_p)), mp_ui(new Ui::IsotopicClusterShaperDlg), mp_programWindow(program_window_p), m_applicationName(applicationName), m_description(description) { if(!program_window_p) qFatal("Programming error. Program aborted."); setupDialog(); // Update the window title because the window title element in m_ui might be // either erroneous or empty. setWindowTitle(QString("%1 - %2").arg(applicationName).arg(description)); m_normalizingIntensity = normalizing_intensity; mp_ui->normalizingIntensitySpinBox->setValue(normalizing_intensity); // Logically if setting the normalizing intensity, then activate the group // box. mp_ui->normalizationGroupBox->setChecked(true); setIsotopiCluster(isotopic_cluster_sp); } /*! \fn IsotopicClusterShaperDlg::closeEvent(QCloseEvent *event) Upon closing of the dialog window (unused \a event), writes the settings to the application configuration file. */ void IsotopicClusterShaperDlg::closeEvent(QCloseEvent *event) { Q_UNUSED(event) // qDebug(); writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Destructs this IsotopicClusterShaperDlg instance. */ IsotopicClusterShaperDlg::~IsotopicClusterShaperDlg() { writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); delete mp_ui; } /*! \brief Saves the settings of this dialog window to the application configuration file (\a config_settings_file_path). The saved configuration is read from the file to set back the dialog window in the same status. */ void IsotopicClusterShaperDlg::writeSettings( const QString &config_settings_file_path) { // qDebug(); // First save the config widget state. mp_massPeakShaperConfigWidget->writeSettings(config_settings_file_path); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("IsotopicClusterShaperDlg"); settings.setValue("geometry", saveGeometry()); settings.setValue("charge", mp_ui->ionChargeSpinBox->value()); settings.setValue("splitter", mp_ui->splitter->saveState()); settings.endGroup(); } /*! \brief Reads the settings of this dialog window from the application configuration file (\a config_settings_file_path). The configuration is read from the file to set back the dialog window in the same status. */ void IsotopicClusterShaperDlg::readSettings(const QString &config_settings_file_path) { // qDebug(); // First read the config widget state. mp_massPeakShaperConfigWidget->readSettings(config_settings_file_path); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("IsotopicClusterShaperDlg"); restoreGeometry(settings.value("geometry").toByteArray()); mp_ui->ionChargeSpinBox->setValue(settings.value("charge", 1).toInt()); mp_ui->splitter->restoreState(settings.value("splitter").toByteArray()); settings.endGroup(); } /*! \brief Sets up the dialog window. */ void IsotopicClusterShaperDlg::setupDialog() { mp_ui->setupUi(this); // We want to destroy the dialog when it is closed. setAttribute(Qt::WA_DeleteOnClose); // Setup the peak shaper configuration widget inside of the frame QVBoxLayout *v_box_layout_p = new QVBoxLayout(this); // Trasmit the config member object reference so that it can be modified by // the configuration widget. mp_massPeakShaperConfigWidget = new MassPeakShaperConfigWidget(m_config, this); v_box_layout_p->addWidget(mp_massPeakShaperConfigWidget); mp_ui->peakShapeConfigFrame->setLayout(v_box_layout_p); // Set the message timer to be singleShot. This time is used to erase the text // shown in the message line edit widget after 3 seconds. msp_msgTimer->setInterval(3000); msp_msgTimer->setSingleShot(true); connect(msp_msgTimer.get(), &QTimer::timeout, [this]() { mp_ui->messageLineEdit->setText(""); }); connect(mp_ui->normalizationGroupBox, &QGroupBox::toggled, this, &IsotopicClusterShaperDlg::normalizingGrouBoxToggled); connect(mp_ui->normalizingIntensitySpinBox, &QSpinBox::valueChanged, this, &IsotopicClusterShaperDlg::normalizingIntensityValueChanged); // Set the default color of the mass spectra trace upon mass spectrum // synthesis QColor color("black"); // Now prepare the color in the form of a QByteArray QDataStream stream(&m_colorByteArray, QIODevice::WriteOnly); stream << color; // The color button connect(mp_ui->colorSelectorPushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::traceColorPushButtonClicked); // The values above are actually set in readSettings (with default values if // missing in the config settings.) readSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); // Always start the dialog with the first page of the tab widget, // the input data page. mp_ui->tabWidget->setCurrentIndex( static_cast(TabWidgetPage::INPUT_DATA)); // Throughout of *this* dialog window, the normalization factor // for the peak shapes (Gaussian, specifically) is going to be 1. connect(mp_ui->importPushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::importFromText); connect(mp_ui->checkParametersPushButton, &QPushButton::clicked, this, qOverload<>( &IsotopicClusterShaperDlg::checkParameters)); connect(mp_ui->runPushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::run); connect(mp_ui->outputFilePushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::outputFileName); connect(mp_ui->displayMassSpectrumPushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::displayMassSpectrum); connect(mp_ui->copyMassSpectrumToClipboardPushButton, &QPushButton::clicked, this, &IsotopicClusterShaperDlg::copyMassSpectrumToClipboard); } /*! \brief Returns a string with the mass spectrum name. The mass spectrum is the result of the isotopic cluster shaping process. */ QString IsotopicClusterShaperDlg::craftMassSpectrumName() { // The user might have forgotten to insert a preferred name in the mass // spectrum file name edit widget. We thus craft one on the basis of the // centroid peaks and the current time. QString name = mp_ui->massSpectrumNameResultsLineEdit->text(); if(name.isEmpty()) { name = QString("%1-%2 @ %3") .arg(m_peakShapers.at(0)->getPeakCentroid().x) .arg(m_peakShapers.at(m_peakShapers.size() - 1)->getPeakCentroid().x) .arg(QTime::currentTime().toString("hh:mm:ss.zzz")); } else { name.append(" @ "); name.append(QTime::currentTime().toString("hh:mm:ss.zzz")); } // qDebug() << "Returning mass spectrum name:" << name; return name; } /*! \brief Creates all the peak shapers required to process all the peak centroids in the isotopic cluster. */ std::size_t IsotopicClusterShaperDlg::fillInThePeakShapers() { // qDebug(); // Clear the peak shapers that might have been created in a previous run. m_peakShapers.clear(); int charge = mp_ui->ionChargeSpinBox->value(); // We compute the m/z value below, so z cannot be 0. if(charge <= 0) qFatal( "Programming error. By this time the charge should have been validated > " "0"); for(auto data_point : m_isotopicCluster) { // We need to account for the charge. Note the division below. m_peakShapers.append(std::make_shared( pappso::DataPoint(data_point.x / charge, data_point.y), m_config)); } // qDebug() << "m_config:" << m_config.asText(800); message( QString("The number of peak centroids is: %1").arg(m_peakShapers.size())); return m_peakShapers.size(); } /*! \brief Checks that the configuration parameters are correct. Any encountered error is appended to \a error_list. */ bool IsotopicClusterShaperDlg::checkParameters( libXpertMassCore::ErrorList &error_list) { qDebug(); // The general idea is that a number of parameters are inter-dependent. We // need to check these parameters in a precise order because of these // inter-dependencies. // Remove all the text in the log text edit widget. mp_ui->logPlainTextEdit->clear(); // Remove all text in the results text edit widget. mp_ui->resultPlainTextEdit->clear(); QString msg; // Make sure we have some (mz,i) values to crunch. if(!m_isotopicCluster.size()) { msg = "There are no centroid peaks to process.\n"; message(msg); error_list.append(msg); qDebug() << msg; return false; } if(m_referencePeakMz <= 0) { msg = "The reference peak m/z value is not set. Please enter one m/z value."; message(msg); error_list.append(msg); qDebug() << msg; return false; } //////////////////// THE ION CHARGE ///////////////////// int charge = mp_ui->ionChargeSpinBox->value(); // m/z has charge at the denominator! if(charge <= 0) { msg = "The ion charge must be >= 1"; message(msg, 6000); error_list.append(msg); qDebug() << msg; return false; } /////////////////// Ask the widget to perform the check /////////////////// libXpertMassCore::ErrorList widget_error_list; if(!mp_massPeakShaperConfigWidget->checkParameters(&error_list)) { msg = QString("There were errors in the configuration:\n%1") .arg(libXpertMassCore::Utils::joinErrorList(widget_error_list, "\n")); message(msg, 100000); error_list.append(msg); qDebug() << msg; return false; } // No errors ? Then output the configuration. qDebug().noquote() << mp_massPeakShaperConfigWidget->getConfig(); m_config.initialize(*mp_massPeakShaperConfigWidget->getConfig()); QString text = m_config.toString(); qDebug().noquote() << "The configuration:\n" << text; mp_ui->logPlainTextEdit->appendPlainText(text); // Finally do a resolve of all the config bits to something actually useful. libXpertMassCore::ErrorList resolve_error_list; if(!m_config.resolve(resolve_error_list)) { msg = QString("Failed to finally resolve the configuration:\n%1") .arg(libXpertMassCore::Utils::joinErrorList(resolve_error_list, "\n")); message(msg, 6000); error_list.append(msg); qDebug() << msg; return false; } // At this point, because we have set all the relevant values to the // m_config PeakShapeConfig, we can create the PeakShaper instances and set // to them the m_config. if(!fillInThePeakShapers()) { QMessageBox::warning( 0, tr("Peak shaper (Gaussian or Lorentzian)"), tr("Please, insert at least one (m/z i) pair in the following " "format:\n\n" "\n\n" "With being any non numerical character \n" "(space or non-'.' punctuation, for example).\n\n" "Once the data have been pasted in the edit widget, click the " "'Import from text' button above.\n" "Also, make sure that you fill-in the resolution or the FWHM " "value and the ion charge."), QMessageBox::Ok); return false; } mp_ui->logPlainTextEdit->appendPlainText( QString("Number of input peak centroids to process: %1.\n") .arg(m_peakShapers.size())); mp_ui->logPlainTextEdit->appendPlainText(m_config.toString()); message("The configuration validated fine. Check the LOG tab for details."); // At this point we'll have some things to report to the LOG tab, // switch to it now. // mp_ui->tabWidget->setCurrentIndex(static_cast(TabWidgetPage::LOG)); // qDebug() << "Now returning true after having checked the parameters."; return true; } /*! \brief Checks that the configuration parameters are correct. \sa checkParameters(libXpertMassCore::ErrorList &error_list) */ bool IsotopicClusterShaperDlg::checkParameters() { libXpertMassCore::ErrorList error_list; return checkParameters(error_list); } /*! \brief Set \a message to the message line edit widget with \a timeout. At the end of the time out the message is erased. */ void IsotopicClusterShaperDlg::message(const QString &message, int timeout) { mp_ui->messageLineEdit->setText(message); msp_msgTimer->stop(); msp_msgTimer->setInterval(timeout); msp_msgTimer->start(); } /*! \brief Runs the computations. */ void IsotopicClusterShaperDlg::run() { qDebug(); libXpertMassCore::ErrorList error_list; if(!checkParameters(error_list)) { QMessageBox::warning( 0, tr("Isotopic cluster shaper"), QString("The parameters check failed with errors:\n%1") .arg(libXpertMassCore::Utils::joinErrorList(error_list, "\n")), QMessageBox::Ok); return; } // At this point all the data are set, we can start the computation on all // the various peak shapers. double minMz = std::numeric_limits::max(); double maxMz = std::numeric_limits::min(); // Clear the map trace that will receive the results of the combinations. m_mapTrace.clear(); // Now actually shape a peak around each peak centroid (contained in each // peak shaper). int processed = 0; for(auto peak_shaper_sp : m_peakShapers) { if(!peak_shaper_sp->computePeakShape()) { QMessageBox::warning( 0, tr("mineXpert: Peak shaper (Gaussian or Lorentzian)"), QString("Failed to compute a Trace for peak shape at m/z %1.") .arg(peak_shaper_sp->getPeakCentroid().x, QMessageBox::Ok)); return; } // Now that we have computed the full shape around the centroid, we'll // be able to extract the smallest and greatest mz values of the whole // shape. We will need these two bounds to craft the bins in the // MzIntegrationParams object. if(peak_shaper_sp->getTrace().size()) { double smallestMz = peak_shaper_sp->getTrace().front().x; minMz = std::min(smallestMz, minMz); double greatestMz = peak_shaper_sp->getTrace().back().x; maxMz = std::max(greatestMz, maxMz); } ++processed; } // Now create the actual spectrum by combining all the shapes into a cluster // shape. if(m_config.isWithBins()) { // Bins were requested. // There are two situations: // 1. The bin size is fixed. // 2. The bin size is dynamically calculated on the basis of the // resolution and of the bin size divisor. double bin_size = m_config.getBinSize(); if(m_config.getBinSizeFixed()) { // The bin size is fixed, easy situation. qDebug() << "The bin size is FIXED."; m_mzIntegrationParams.initialize( minMz, maxMz, pappso::MzIntegrationParams::BinningType::ARBITRARY, pappso::PrecisionFactory::getDaltonInstance(bin_size), /*binSizeDivisor, 1 = no-op*/ 1, /*decimalPlacesr*/ -1, true); } else { // The bin size is dynamically calculated. if(m_config.getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::FWHM) { qDebug() << "The mass peak width logic is FWHM."; m_mzIntegrationParams.initialize( minMz, maxMz, pappso::MzIntegrationParams::BinningType::ARBITRARY, pappso::PrecisionFactory::getDaltonInstance(bin_size), 1, -1, true); } else if(m_config.getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::RESOLUTION) { qDebug() << "The mass peak width logic is RESOLUTION."; m_mzIntegrationParams.initialize( minMz, maxMz, pappso::MzIntegrationParams::BinningType::ARBITRARY, pappso::PrecisionFactory::getResInstance( m_config.getResolution()), m_config.getBinSizeDivisor(), -1, true); } else qFatal( "Programming error. At this point, the peak width logic should " "have been set."); } // Now compute the bins. std::vector bins = m_mzIntegrationParams.createBins(); // We will need to perform combinations, positive combinations. pappso::MassSpectrumPlusCombiner mass_spectrum_plus_combiner; mass_spectrum_plus_combiner.setBins(bins); for(auto mass_peak_shaper_sp : m_peakShapers) { mass_spectrum_plus_combiner.combine(m_mapTrace, mass_peak_shaper_sp->getTrace()); } } else { // No bins were required. We can combine simply: qDebug() << "NO BINS requested."; pappso::TracePlusCombiner trace_plus_combiner(-1); for(auto mass_peak_shaper_sp : m_peakShapers) { // The combiner does not handle any data point for which y = 0. // So the result trace does not have a single such point; trace_plus_combiner.combine(m_mapTrace, mass_peak_shaper_sp->getTrace()); } } message(QString("Successfully processed %1 peak centroids").arg(processed)); // This is actually where the normalization needs to be performed. The // user might have asked that the apex of the shape be at a given // intensity. This is actually true when the IsoSpec++-based calculations // have been performed, that is the peak centroids have the expected // intensities normalized to the expected value. But then we have binned // all the numerous peak centroids into bins that are way larger than the // mz_step above. This binning is crucial to have a final spectrum that // actually looks like a real one. But it comes with a drawback: the // intensities of the m/z bins are no more the one of the initial // centroids, they are way larger. So we need to normalize the obtained // trace. m_finalTrace.clear(); if(mp_ui->normalizationGroupBox->isChecked()) { m_normalizingIntensity = mp_ui->normalizingIntensitySpinBox->value(); // qDebug() << "Now normalizing to intensity = " << // m_normalizingIntensity; pappso::Trace trace = m_mapTrace.toTrace(); m_finalTrace = trace.filter( pappso::FilterNormalizeIntensities(m_normalizingIntensity)); // double max_int = normalized_trace.maxYDataPoint().y; // qDebug() << "After normalization max int:" << max_int; } else m_finalTrace = m_mapTrace.toTrace(); mp_ui->resultPlainTextEdit->appendPlainText(m_finalTrace.toString()); // Now switch to the page that contains these results: // Provide the title of the mass spectrum there for the user to have a last // opportunity to change it. mp_ui->massSpectrumNameResultsLineEdit->setText( mp_ui->massSpectrumTitleLineEdit->text()); mp_ui->tabWidget->setCurrentIndex(static_cast(TabWidgetPage::RESULTS)); } /*! \brief Provides the user with a dialog for selecting a file name where to output the results of the computation. */ void IsotopicClusterShaperDlg::outputFileName() { m_fileName = QFileDialog::getSaveFileName( this, tr("Export to text file"), QDir::homePath(), tr("Any file type(*)")); } /*! \brief Crafts a mass spectrum name and emits a signal to display the mass spectrum. */ void IsotopicClusterShaperDlg::displayMassSpectrum() { // We want that the process going on from now on has an unambiguous // file/sample name, so we craft the name here. // Finally, publish the mass spectrum. // qDebug() << "Going to write this trace:" << m_finalTrace.toString(); QString title = craftMassSpectrumName(); emit displayMassSpectrumSignal( title, m_colorByteArray, std::make_shared(m_finalTrace)); } /*! \brief Copies the mass spectrum to the clipboard. The mass spectrum is the result of the combination of all the peak shapes into a single trace. */ void IsotopicClusterShaperDlg::copyMassSpectrumToClipboard() { // Simply copy the results shown in the text edit widget to the clipboard. // We cannot use m_mapTrace for that because if normalization was asked for, // then we would not see that in m_mapTrace (normalization is local). QString trace_text = mp_ui->resultPlainTextEdit->toPlainText(); if(trace_text.isEmpty()) { message("The mass spectrum is empty."); return; } QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(trace_text); } /*! \brief Selects a color for the mass spectrum trace to be plotted. */ void IsotopicClusterShaperDlg::traceColorPushButtonClicked() { // Allow (true) the user to select a color that has been chosen already. QColor color = ColorSelector::chooseColor(true); if(!color.isValid()) return; // Store the color as a QByteArray in the member datum. QDataStream stream(&m_colorByteArray, QIODevice::WriteOnly); stream << color; // Update the color of the button. QPushButton *colored_push_button = mp_ui->colorSelectorPushButton; colorizePushButton(colored_push_button, m_colorByteArray); } /*! \brief Sets the \a isotopic_cluster_sp isotopic cluster that is the starting material for the computations. */ void IsotopicClusterShaperDlg::setIsotopiCluster( pappso::TraceCstSPtr isotopic_cluster_sp) { // Start by copying the centroids locally in the form of a Trace collecting // DataPoint objects. m_isotopicCluster.assign(isotopic_cluster_sp->begin(), isotopic_cluster_sp->end()); // Now create the text to display that in the text edit widget. QString result_as_text; // Convert the data into a string that can be displayed in the text edit // widget. // Reset the value to min so that we can test for new intensities below. double greatestIntensity = std::numeric_limits::min(); for(auto data_point : m_isotopicCluster) { result_as_text += QString("%1 %2\n") .arg(data_point.x, 0, 'f', 30) .arg(data_point.y, 0, 'f', 30); // Store the peak centroid that has the greatest intensity. // Used to compute the FWHM value starting from resolution. And // vice-versa. if(data_point.y > greatestIntensity) { m_referencePeakMz = data_point.x; greatestIntensity = data_point.y; } } // qDebug() << "Reference (most intense) peak's m/z value:" << // m_referencePeakMz; m_config.setReferencePeakMz(m_referencePeakMz); mp_massPeakShaperConfigWidget->setReferencePeakMz(m_referencePeakMz); mp_ui->inputDataPointsPlainTextEdit->setPlainText(result_as_text); } /*! \brief Sets \a title as the mass spectrum title to the line edit widget. */ void IsotopicClusterShaperDlg::setMassSpectrumTitle(const QString &title) { mp_ui->massSpectrumTitleLineEdit->setText(title); } /*! \brief Sets the \a color_byte_array color. Colorizes the push button to provide a visual feedback of the color that was selected. */ void IsotopicClusterShaperDlg::setColorByteArray(QByteArray color_byte_array) { m_colorByteArray = color_byte_array; // Update the color of the button. colorizePushButton(mp_ui->colorSelectorPushButton, color_byte_array); } /*! \brief Colorizes the \a push_button_p push button with the color encoded in \a color_byte_array. */ void IsotopicClusterShaperDlg::colorizePushButton(QPushButton *push_button_p, QByteArray color_byte_array) { QColor color; QDataStream stream(&color_byte_array, QIODevice::ReadOnly); stream >> color; if(color.isValid()) { QPalette palette = push_button_p->palette(); palette.setColor(QPalette::Button, color); push_button_p->setAutoFillBackground(true); push_button_p->setPalette(palette); push_button_p->update(); // Now prepare the color in the form of a QByteArray QDataStream stream(&m_colorByteArray, QIODevice::WriteOnly); stream << color; } } /*! \brief Imports the peak centrois from the text widget and make a pappso::Trace out of them that is then set as the isotopic cluster. \setIsotopiCluster() */ void IsotopicClusterShaperDlg::importFromText() { // We have peak centroids as in the text edit widget // // 59.032643588680002721957862377167 0.021811419157258506856811308694 // // and we want to import them. QString peak_centroids_text = mp_ui->inputDataPointsPlainTextEdit->toPlainText(); // Easily transfom that into a Trace pappso::Trace trace(peak_centroids_text); pappso::TraceSPtr temp_cluster_sp = std::make_shared(); for(auto datapoint : trace) { temp_cluster_sp->append(datapoint); } // Finally do the real import. setIsotopiCluster(temp_cluster_sp); message("The data were imported. Please check the import results."); } /*! \brief Signals that the normalize intensity value has changed. The new value is set to the member datum m_normalizingIntensity. */ void IsotopicClusterShaperDlg::normalizingIntensityValueChanged() { m_normalizingIntensity = mp_ui->normalizingIntensitySpinBox->value(); } /*! \brief Signals that the the normalization group box was toggled. If the group box is checked, the normalizing intensity is read from the spin box widget and set to the member datum m_normalizingIntensity. Otherwise, the member datum m_normalizingIntensity is set to std::numeric_limits::min(). */ void IsotopicClusterShaperDlg::normalizingGrouBoxToggled(bool checked) { if(!checked) m_normalizingIntensity = std::numeric_limits::min(); else m_normalizingIntensity = mp_ui->normalizingIntensitySpinBox->value(); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/IsotopicDataTableView.cpp000664 001750 001750 00000012006 15100504560 027010 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include /////////////////////// Qt includes #include #include #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassGui/IsotopicDataTableView.hpp" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::IsotopicDataTableView \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile IsotopicDataTableView.hpp \brief The IsotopicDataTableView class provides a table view widget to display isotopic data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableView::mp_parent \brief The parent widget. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableView::msp_isotopicData \brief The isotopic data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableView::m_dragStartPos \brief The start drag position. */ /*! \brief Constructs a IsotopicDataTableView instance with \a parent_p widget. */ IsotopicDataTableView::IsotopicDataTableView(QWidget *parent_p) : QTableView(parent_p), mp_parent(parent_p) { setAlternatingRowColors(true); setTextElideMode(Qt::ElideMiddle); setSortingEnabled(false); setSelectionMode(QAbstractItemView::ContiguousSelection); // Select items, not only rows or columns setSelectionBehavior(QAbstractItemView::SelectItems); QHeaderView *headerView = horizontalHeader(); headerView->setSectionsClickable(true); headerView->setSectionsMovable(true); } /*! \brief Destructs this IsotopicDataTableView instance. */ IsotopicDataTableView::~IsotopicDataTableView() { } /*! \brief Sets the isotopic data \a isotopic_data_sp. */ void IsotopicDataTableView::setIsotopicData(libXpertMassCore::IsotopicDataSPtr isotopic_data_sp) { msp_isotopicData = isotopic_data_sp; } /*! \brief Returns the isotopic data. */ libXpertMassCore::IsotopicDataSPtr IsotopicDataTableView::getIsotopicData() { return msp_isotopicData; } /*! \brief Sets the parent widget \a parent_p. */ void IsotopicDataTableView::setParent(QWidget *parent_p) { Q_ASSERT(parent_p); mp_parent = parent_p; } /*! \brief Returns the parent widget. */ QWidget * IsotopicDataTableView::parent() { return mp_parent; } /*! \brief Reacts to a selection change in the table view. The \a selected parameter lists item that have undergone selection during a (de)selection operation, not the items that have been effectively committed to selection (release of key or release of mouse drag). Likewise for \a deselected. This function elaborates a list of selected rows (not selected \e cells) and emits the selectedRowsChangedSignal with the set of selected rows. */ void IsotopicDataTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { // The selected selection parameter lists item that have undergone selection // during a (de)selection operation, not the items that have been effectively // committed to selection (release of key or release of mouse drag). emit selectionChangedSignal(selected, deselected); // Take advantage to emit a signal with a list of effectively committed // selected indices. emit allSelectedIndicesSignal(selectedIndexes()); // Finally, take advantage to actually determine which unique rows do harbor // the selected indices (selection indices might contain multiple cells from a // given single row!) QModelIndexList selected_indices = selectedIndexes(); std::set rows_set; for(int iter = 0; iter < selected_indices.size(); ++iter) { rows_set.insert(selected_indices.at(iter).row()); } emit selectedRowsChangedSignal(rows_set); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/IsotopicDataTableViewModel.cpp000664 001750 001750 00000041535 15100504560 030002 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes #include /////////////////////// Qt includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Isotope.hpp" #include "MsXpS/libXpertMassGui/IsotopicDataTableViewModel.hpp" #include "MsXpS/libXpertMassGui/IsotopicClusterGeneratorDlg.hpp" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::IsotopicDataTableViewModel \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile IsotopicDataTableViewModel.hpp \brief The IsotopicDataTableViewModel class provides a table view widget to display isotopic data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableViewModel::mp_parent \brief The parent widget. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableViewModel::msp_isotopicData \brief The isotopic data. */ /*! \variable MsXpS::libXpertMassGui::IsotopicDataTableViewModel::mp_tableView \brief The table view widget. */ /*! \brief Constructs a IsotopicDataTableViewModel instance. \list \li \a parent_p: the parent widget. \li \a isotopic_data_sp: the isotopic data. \endlist */ IsotopicDataTableViewModel::IsotopicDataTableViewModel( QObject *parent_p, libXpertMassCore::IsotopicDataSPtr isotopic_data_sp) : QAbstractTableModel(parent_p), msp_isotopicData(isotopic_data_sp) { if(!parent_p) qFatal("Programming error."); mp_parent = dynamic_cast(parent_p); connect(this, &IsotopicDataTableViewModel::editCompletedSignal, this, &IsotopicDataTableViewModel::editCompleted); } /*! \brief Destructs this IsotopicDataTableViewModel instance. */ IsotopicDataTableViewModel::~IsotopicDataTableViewModel() { } /*! \brief Returns the parent widget. */ const QWidget * IsotopicDataTableViewModel::parent() const { return mp_parent; } /*! \brief Sets the table view widget \a table_view_p. */ void IsotopicDataTableViewModel::setTableView(IsotopicDataTableView *table_view_p) { if(!table_view_p) qFatal("Programming error."); mp_tableView = table_view_p; } /*! \brief Returns the table view widget. */ IsotopicDataTableView * IsotopicDataTableViewModel::tableView() { return mp_tableView; } /*! \brief Returns the count of rows in this table view. \a parent is unused. */ int IsotopicDataTableViewModel::rowCount( [[maybe_unused]] const QModelIndex &parent) const { return msp_isotopicData->size(); } /*! \brief Returns the count of columns in this table view. \a parent is unused. */ int IsotopicDataTableViewModel::columnCount( [[maybe_unused]] const QModelIndex &parent) const { return static_cast(libXpertMassCore::IsotopeFields::LAST); } /*! \brief Returns true if the row could be inserted at reference model index \a parent. The insertion occurs at the index right before \a row such that the newly inserted row will be effectively at \a row index. The cells of the inserted row will be set with default values. */ bool IsotopicDataTableViewModel::insertRow(int row, const QModelIndex &parent) { // The insertion occurs at index right *before* the row parameter. int first = row; int last = first; qDebug() << "Preparing to insert before index:" << row; beginInsertRows(parent, first, last); libXpertMassCore::IsotopeQSPtr isotope_qsp = QSharedPointer::create( "NOT_SET", "NOT_SET", 0.000000000, 1.00000000); // false: do not update the mass maps as the newly inserted isotope is fake. // insert above row in the isotope vector. bool res = msp_isotopicData->insertNewIsotope(isotope_qsp, row, false); endInsertRows(); // Because we want to edit the newly inserted row, select it. mp_tableView->selectRow(row); emit modifiedSignal(); return res; } /*! \brief Returns the header data for \a section. If \a orientation is Qt::Vertical returns the row number; if \a orientation is Qt::Horizontal, returns the text in the header. If \a role is not Qt::DisplayRole, returns QVariant(). */ QVariant IsotopicDataTableViewModel::headerData(int section, Qt::Orientation orientation, int role) const { if(role != Qt::DisplayRole) return QVariant(); if(orientation == Qt::Vertical) { // Return the row number. QString valueString; valueString.setNum(section); } else if(orientation == Qt::Horizontal) { // Return the header of the column. switch(section) { case static_cast(libXpertMassCore::IsotopeFields::NAME): return "Element"; case static_cast(libXpertMassCore::IsotopeFields::SYMBOL): return "Symbol"; case static_cast(libXpertMassCore::IsotopeFields::MASS): return "Mass"; case static_cast(libXpertMassCore::IsotopeFields::PROBABILITY): return "Prob."; default: qFatal("Programming error."); } } // Should never get here. return QVariant(); } /*! \brief Returns the data for cell at \a index, for \a role. If \a role is Qt::TextAlignmentRole, returns (Qt::AlignRight | Qt::AlignVCenter). If \a role is Qt::DisplayRole, returns the text in the cell described in \a index in the form of a QVariant. */ QVariant IsotopicDataTableViewModel::data(const QModelIndex &index, int role) const { if(!index.isValid()) return QVariant(); if(role == Qt::TextAlignmentRole) { return int(Qt::AlignRight | Qt::AlignVCenter); } else if(role == Qt::DisplayRole) { int row = index.row(); int column = index.column(); // Let's get the data for the right column and the right // row. Let's find the row first, so that we get to the proper // entity. libXpertMassCore::IsotopeQSPtr isotope_qsp = msp_isotopicData->getIsotopes().at(row); // Now see what's the column that is asked for. Prepare a // string that we'll feed with the right data before returning // it as a QVariant. QString value_string; if(column == static_cast(libXpertMassCore::IsotopeFields::NAME)) { value_string = isotope_qsp->getName(); } else if(column == static_cast(libXpertMassCore::IsotopeFields::SYMBOL)) { value_string = isotope_qsp->getSymbol(); } else if(column == static_cast(libXpertMassCore::IsotopeFields::MASS)) { value_string = QString("%1").arg(isotope_qsp->getMass(), 0, 'f', 12); } else if(column == static_cast(libXpertMassCore::IsotopeFields::PROBABILITY)) { value_string = QString("%1").arg(isotope_qsp->getProbability(), 0, 'f', 60); } else { qFatal("Programming error."); } return value_string; } // End of // else if(role == Qt::DisplayRole) return QVariant(); } /*! \brief Set the data to \a value for the cell described by \a index. If \a role is not Qt::EditRole, returns false. Returns true if \a value could be converted to the right type depending on the column of the cell, true otherwise. The data in the cell described by \a index is read, converted to the right type and set to the matching isotope instance. The row datum in \a index points to the index of the isotope in the isotopica data. If conversion from QVariant to the proper data type fails, returns false, otherwise returns true. */ bool IsotopicDataTableViewModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(role != Qt::EditRole) return false; if(!checkIndex(index)) return false; // We want to access the isotope in the isotopic data. This is done by using // the index, that is, the row. int isotopic_data_index = index.row(); int column = index.column(); // Store the original isotope pointer! libXpertMassCore::IsotopeQSPtr old_isotope_sp = msp_isotopicData->getIsotopes().at(isotopic_data_index); // Prepare a copy that we'll modify the proper field of. libXpertMassCore::IsotopeQSPtr new_isotope_sp = QSharedPointer::create(*old_isotope_sp); // Now determine what is the column of the cell (that is, the field) that was // modified and modify the new_isotope_sp accordingly. bool ok = false; if(column == static_cast(libXpertMassCore::IsotopeFields::NAME)) { new_isotope_sp->setName(value.toString()); } else if(column == static_cast(libXpertMassCore::IsotopeFields::SYMBOL)) { new_isotope_sp->setSymbol(value.toString()); } else if(column == static_cast(libXpertMassCore::IsotopeFields::MASS)) { double new_value = value.toDouble(&ok); if(!ok) return false; new_isotope_sp->setMass(new_value); } else if(column == static_cast(libXpertMassCore::IsotopeFields::PROBABILITY)) { double new_value = value.toDouble(&ok); if(!ok) return false; new_isotope_sp->setProbability(new_value); } else { qFatal("Programming error."); } // At this point we know the new isotope has fine data in it, just replace the // old with the new. msp_isotopicData->replace(old_isotope_sp, new_isotope_sp); // emit editCompleted(new_isotope_sp->toString()); // Not sure if this is really necessary. emit(dataChanged(index, index)); emit modifiedSignal(); return true; } void IsotopicDataTableViewModel::refreshAfterNewData() { // This is bad code to ensure that the table view items are refreshed // throughout all the data set. QModelIndex parent; beginInsertRows(parent, 0, rowCount() - 1); endInsertRows(); emit modifiedSignal(); } /*! \brief Removes all the rows from the table view. Returns true if rows were effectively removed, false otherwise. */ bool IsotopicDataTableViewModel::clearAllData() { // How many rows do we have? int row_count = rowCount(); if(!row_count) // We did not remove any Isotope from the IsotopicData return false; // The begin_index and end_index are *inclusive* in the beginRemoveRows() // call below. beginRemoveRows(QModelIndex(), 0, row_count - 1); // true: update the maps because we need to clear all the items there. msp_isotopicData->eraseIsotopes(0, row_count - 1, true); endRemoveRows(); emit modifiedSignal(); // We did remove items. return true; } /*! \brief Returns the flags for the \a index. The returned flags include Qt::ItemIsEditable. */ Qt::ItemFlags IsotopicDataTableViewModel::flags(const QModelIndex &index) const { return Qt::ItemIsEditable | QAbstractTableModel::flags(index); } /*! \brief Signals that the editing of a cell is completed with final \a text. */ void IsotopicDataTableViewModel::editCompleted(const QString &text) { qDebug() << "After editing the new isotope:" << text; emit modifiedSignal(); } /*! \brief Inserts a new row above the current index. */ void IsotopicDataTableViewModel::insertIsotopeRowAbove() { // We need to maintain a perfect correlation between the data in the // IsotopicData object and the rows in the table view. int insertion_row_index = 0; // We want to add a row above the current index. Note that all the functions // dealing with models and containers insert above the index provided to the // insertion function. int row_count = rowCount(); if(!row_count) { // qDebug() << "There are no isotopes."; // We do not care, because we cannot, if there is a current index. } else { // qDebug() << "There are currently" << row_count << "isotopes in the // table"; // In this case, we want to know if there is a current index. QItemSelectionModel *selection_model = mp_tableView->selectionModel(); QModelIndex current_index = selection_model->currentIndex(); if(!current_index.isValid()) { return; } insertion_row_index = current_index.row(); // qDebug() << "The current index row is at index" << insertion_row_index; } // insertRow() inserts a single row before the given row in the child items of // the parent specified. This means that we just use that row index without // modifying it. insertRow(insertion_row_index); emit modifiedSignal(); } /*! \brief Inserts a new row below the current index. */ void IsotopicDataTableViewModel::insertIsotopeRowBelow() { // We need to maintain a perfect correlation between the data in the // IsotopicData object and the rows in the table view. int insertion_row_index = 0; // We want to add a row below the current index, but beware that all the // functions dealing with models and containers insert *above* the index // provided to the insertion function. int row_count = rowCount(); if(!row_count) { // qDebug() << "There are no isotopes."; // We do not care, because we cannot, if there is a current index. } else { // qDebug() << "There are currently" << row_count << "isotopes in the // table"; // In this case, we want to know if there is a current index. QItemSelectionModel *selection_model = mp_tableView->selectionModel(); QModelIndex current_index = selection_model->currentIndex(); if(!current_index.isValid()) { return; } insertion_row_index = current_index.row(); // qDebug() << "The current index row is at index" << insertion_row_index; } // insertRow() inserts a single row before the given row in the child items of // the parent specified. This means that we'll have to increment the // insertion_row_index by 1. ++insertion_row_index; insertRow(insertion_row_index); emit modifiedSignal(); } /*! \brief Removes all the selected rows. */ void IsotopicDataTableViewModel::removeSelected() { // We need to maintain a perfect correlation between the data in the // IsotopicData object and the rows in the table view. // int row_count = rowCount(); // qDebug() << "There are currently" << row_count //<< "isotopes in the table view model."; QItemSelectionModel *selection_model = mp_tableView->selectionModel(); QModelIndexList selected_indices = selection_model->selectedIndexes(); // qDebug() << "The number of selected indices:" << selected_indices.size(); // We know that the indices list might contain multiple indices for the same // row. We need to extract a unique list of rows for which at least one index // is selected. Then, we'll use that list of rows to perform the removal. std::set selected_rows; for(int iter = 0; iter < selected_indices.size(); ++iter) { selected_rows.insert(selected_indices.at(iter).row()); } if(!selected_rows.size()) { // qDebug() << "There are not selected rows in the table view."; return; } int begin_index = *selected_rows.begin(); // The end index is at position end() - 1 of the std::set. int end_index = *std::prev(selected_rows.end()); // qDebug() << "The rows to be removed are in the fully inclusive index range: // [" //<< begin_index << "-" << end_index << "]."; // The begin_index and end_index are *inclusive* in the beginRemoveRows() // call below. beginRemoveRows(QModelIndex(), begin_index, end_index); // true: update the maps because the remaining isotopes are valid. msp_isotopicData->eraseIsotopes(begin_index, end_index, true); endRemoveRows(); emit modifiedSignal(); // Now select the item that was right below the last selected removed item. if(!rowCount()) { // Do nothing. } else { if(begin_index >= rowCount()) { begin_index = rowCount() - 1; } } mp_tableView->selectRow(begin_index); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptQObjectExposureRecord.cpp000664 001750 001750 00000004545 15100504560 031043 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/JavaScriptQObjectExposureRecord.hpp" namespace MsXpS { namespace libXpertMassGui { JavaScriptQObjectExposureRecord::JavaScriptQObjectExposureRecord( QObject *object_p, QObject *object_parent_p, const QString &object_name, const QString &object_alias, const QString &object_description) : mp_object(object_p), mp_objectParent(object_parent_p), m_objectName(object_name), m_objectAlias(object_alias), m_objectDescription(object_description) { } JavaScriptQObjectExposureRecord::~JavaScriptQObjectExposureRecord() { } QObject * JavaScriptQObjectExposureRecord::getObject() { return mp_object; } QObject * JavaScriptQObjectExposureRecord::getObjectParent() { return mp_objectParent; } QString JavaScriptQObjectExposureRecord::getObjectName() { return m_objectName; } QString JavaScriptQObjectExposureRecord::getObjectAlias() { return m_objectAlias; } QString JavaScriptQObjectExposureRecord::getObjectDescription() { return m_objectDescription; } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptQObjectExposureRegistry.cpp000664 001750 001750 00000011012 15100504560 031420 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes #include "MsXpS/libXpertMassGui/JavaScriptQObjectExposureRegistry.hpp" namespace MsXpS { namespace libXpertMassGui { JavaScriptQObjectExposureRegistry::JavaScriptQObjectExposureRegistry( QObject *parent) : QObject(parent) { } JavaScriptQObjectExposureRegistry::~JavaScriptQObjectExposureRegistry() { } JavaScriptQObjectExposureRecordQSPtr JavaScriptQObjectExposureRegistry::getRecordByObject(QObject *object_p) { Q_ASSERT(object_p != nullptr); foreach(JavaScriptQObjectExposureRecordQSPtr record_qsp, m_records) { if(record_qsp->getObject() == object_p) return record_qsp; } return nullptr; } JavaScriptQObjectExposureRecordQSPtr JavaScriptQObjectExposureRegistry::getRecordByObjectParent(QObject *object_p) { Q_ASSERT(object_p != nullptr); foreach(JavaScriptQObjectExposureRecordQSPtr record_qsp, m_records) { if(record_qsp->getObjectParent() == object_p) return record_qsp; } return nullptr; } JavaScriptQObjectExposureRecordQSPtr JavaScriptQObjectExposureRegistry::getRecordByName(const QString object_name) { foreach(JavaScriptQObjectExposureRecordQSPtr record_qsp, m_records) { if(record_qsp->getObjectName() == object_name) return record_qsp; } return nullptr; } JavaScriptQObjectExposureRecordQSPtr JavaScriptQObjectExposureRegistry::getRecordByAlias(const QString object_alias) { foreach(JavaScriptQObjectExposureRecordQSPtr record_qsp, m_records) { if(record_qsp->getObjectName() == object_alias) return record_qsp; } return nullptr; } /*! \brief Registers \a object_p with all the available data. \list \li \a object_parent_p, the object that is the parent of \a object_p \li \a object_name, the name with which the object is exposed (property name) \li \a object_alias, the alias with which the object can be named \li \a object_description, the description of the object \endlist The only compulsory argument is \a object_p. Returns 0 if no new record was made because one by the object was found already or 1 if a new record was made. If an error occurs -1 is returned. */ int JavaScriptQObjectExposureRegistry::registerQObject( QObject *object_p, QObject *object_parent_p, const QString &object_name, const QString &object_alias, const QString &object_description) { if(getRecordByObject(object_p)) { qWarning() << "One record exists already by the same object"; return 0; } m_records.append(QSharedPointer::create( object_p, object_parent_p, object_name, object_alias, object_description)); connect(object_p, &QObject::destroyed, [&]() { unregisterQObject(object_p); }); return 1; } /*! \brief Removes the records for \a object_p from this registry. Returns the count of removed records. */ int JavaScriptQObjectExposureRegistry::unregisterQObject(QObject *object_p) { qsizetype removed_count = 0; if(object_p == nullptr) { qWarning() << "The passed pointer is nullptr."; return removed_count; } JavaScriptQObjectExposureRecordQSPtr record_qsp = getRecordByObject(object_p); if(record_qsp != nullptr) removed_count = m_records.removeAll(record_qsp); return removed_count; } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptWorker.cpp000664 001750 001750 00000005311 15100504560 026063 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// libXpertMassCore includes #include #include #include #include /////////////////////// libXpertMassGUI includes #include #include #include /////////////////////// local includes #include "MsXpS/libXpertMassGui/JavaScriptWorker.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingEnvironment.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" namespace MsXpS { namespace libXpertMassGui { JavaScriptWorker::JavaScriptWorker(QString script, const JavaScriptingEnvironment *env) : m_script(script), mp_scriptingEnvironment(env) { } void JavaScriptWorker::run() { mp_scriptingEnvironment->initializeJsEngine(); QJSValue value = mp_scriptingEnvironment->getJsEngine()->evaluate(m_script); emit finishedSignal(value); } void JavaScriptWorker::requestAbort() { // qDebug() << "Abort was requested."; mp_scriptingEnvironment->getJsEngine()->globalObject().setProperty( "__abortRequested", QJSValue(true)); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptingEnvironment.cpp000664 001750 001750 00000027307 15100504560 027625 0ustar00rusconirusconi000000 000000 /////////////////////// stdlib includes #include /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes #include /////////////////////// libXpertMass includes #include "MsXpS/libXpertMassCore/jsclassregistrar.h" #include "MsXpS/libXpertMassCore/XpertMassCoreJavaScript.hpp" /////////////////////// libXpertMassGui includes #include "MsXpS/libXpertMassGui/XpertMassGuiJavaScript.hpp" /////////////////////// Local includes #include "MsXpS/libXpertMassGui/JavaScriptingEnvironment.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingGuiUtils.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" namespace MsXpS { namespace libXpertMassGui { JavaScriptingEnvironment::JavaScriptingEnvironment(QObject *parent) : QObject(parent), mp_jsEngine(new QJSEngine(this)), mp_javaScriptingWnd(dynamic_cast(parent)) { } JavaScriptingEnvironment::~JavaScriptingEnvironment() { } bool JavaScriptingEnvironment::initializeJsEngine() const { // qDebug() << "Initializing JS engine."; Q_ASSERT(mp_jsEngine != nullptr); // In very lengthy operations, like in loop with large iteration counts, // the user may try to abort the script that is being run. For this there // are a number of things to put in place properly. // 1. The user puts checkAbort() calls in their own code where they see fit // and take proper decisions on the basis of the return value, like // calling break; if the return value is true. // 2. The code below registers a property that is indeed that checkAbort() // function. The initial value of __abortRequested property is false, // logically. // 3. When the user clicks on the cancel button, the value of __abortRequested // is set to true, which means that calls to checkAbort() will return true. // Create and expose the abort check function // This version is for debugging. // QJSValue abortFunction = mp_jsEngine->evaluate( // "(function() { this.print(\"Checking abort\") ; this.processEvents() ; // print(\"Returning \" + this.__abortRequested) ; return // this.__abortRequested; })"); // mp_jsEngine->globalObject().setProperty("checkAbort", abortFunction); // Create and expose the abort check function // This function first calls processEvents() that is exposed to QJSEngine // from JavaScriptingGuiUtils::processEvents(). QJSValue abortFunction = mp_jsEngine->evaluate( "(function() { this.processEvents() ; return this.__abortRequested; })"); mp_jsEngine->globalObject().setProperty("checkAbort", abortFunction); // Initialize the abort flag mp_jsEngine->globalObject().setProperty("__abortRequested", QJSValue(false)); registerJsConstructorForEachClassInRegistrarMap(mp_jsEngine); // Register the libXpertMass and libXpertMassGui entities. registerOwnJsEntities(mp_jsEngine); // Register the libpappsomspp enums and maybe other things. registerExternalJsEntities("pappsomspp", mp_jsEngine); return true; } QJSValue JavaScriptingEnvironment::evaluate(const QString &script) { if(!mp_jsEngine) return QJSValue(); return evaluate(mp_jsEngine, script); } QJSValue JavaScriptingEnvironment::evaluate(QJSEngine *engine_p, const QString &script) { Q_ASSERT(engine_p != nullptr); return engine_p->evaluate(script); } QJSEngine * JavaScriptingEnvironment::getJsEngine() const { return mp_jsEngine; } bool JavaScriptingEnvironment::exposeQObject(QJSEngine *js_engine_p, const QString &name, const QString &alias, const QString &description, QObject *object_p, QObject *object_parent_p, QJSValue &returned_js_value, QJSEngine::ObjectOwnership ownership) { QString error_string; if(name.isEmpty()) { error_string = "Object name cannot be empty"; emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } if(object_p == nullptr) { error_string = "Cannot expose null object"; emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } // Check thread affinity if(object_p->thread() != this->thread()) { error_string = QString("Object belongs to different thread (object: %1, current: %2)") .arg(object_p->thread() ? QString::number(reinterpret_cast( object_p->thread())) : "null") .arg(this->thread() ? QString::number(reinterpret_cast(this->thread())) : "null"); emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } // Should we consider the passed js engine or the member datum ? QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p != nullptr) { returned_js_value = selected_js_engine_p->newQObject(object_p); // We want JavaScriptOwnership because the object needs to exist as long // as the QJSEngine is alive ! Q_UNUSED(ownership) QJSEngine::setObjectOwnership(object_p, QJSEngine::JavaScriptOwnership); selected_js_engine_p->globalObject().setProperty(name, returned_js_value); if(!alias.isEmpty()) selected_js_engine_p->globalObject().setProperty(alias, returned_js_value); } else { error_string = "No usable JavaScript engine available"; emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } // At this point, we can register the object to the exposed objects registry. // The registry registers the object_p and sets a connection to // QObject::destroyed() so that the object is automatically deregistered upon // its destruction. if(m_registry.registerQObject( object_p, object_parent_p, name, alias, description) == -1) return false; mp_javaScriptingWnd->feedBackForExposedQObject(name, alias, description); return true; } bool JavaScriptingEnvironment::exposeQObject(const QString &name, const QString &alias, const QString &description, QObject *object_p, QObject *object_parent_p, QJSValue &returned_js_value, QJSEngine::ObjectOwnership ownership) { return exposeQObject(mp_jsEngine, name, alias, description, object_p, object_parent_p, returned_js_value, ownership); } bool JavaScriptingEnvironment::exposeJsValueToJsEngine(const QString &name, QJSValue &js_value) const { QString error_string; if(mp_jsEngine == nullptr) { error_string = "No JavaScript engine available"; emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } else mp_jsEngine->globalObject().setProperty(name, js_value); return true; } QJSValue JavaScriptingEnvironment::getExposedJsEngineObjectByName( const QString &name) const { QString error_string; if(mp_jsEngine == nullptr) { error_string = "No JavaScript engine available"; emit errorOccurredSignal(error_string); qWarning() << error_string; return false; } return mp_jsEngine->globalObject().property(name); } void JavaScriptingEnvironment::registerJsConstructorForClassInRegistrarMap( const QString &name_space, const QString &class_name, QJSEngine *js_engine_p) const { QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p == nullptr) qFatal() << "No JavaScript engine is currently available."; qDebug() << "Registering JavaScript constructor for" << name_space << "::" << class_name; MsXpS::registerJsConstructorForNameSpaceClassNameInRegistrarMap( name_space, class_name, mp_jsEngine); } void JavaScriptingEnvironment::registerJsConstructorForClassesInRegistrarMap( const std::vector> &namespace_class_pairs, QJSEngine *js_engine_p) const { QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p == nullptr) qFatal() << "No JavaScript engine is currently available."; for(const std::pair &pair : namespace_class_pairs) registerJsConstructorForClassInRegistrarMap(pair.first, pair.second); qDebug() << "At this point, the registry contains" << MsXpS::getNameSpaceClassNameJsConstructorRegistrarMap().size() << "pairs"; } void JavaScriptingEnvironment::registerJsConstructorForEachClassInRegistrarMap( QJSEngine *js_engine_p) const { QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p == nullptr) qFatal() << "No JavaScript engine is currently available."; qDebug() << "Registering the constructor of the classes marked for JS."; // All the classes deemed to be exposed to QJSEngine have a // REGISTER_JS_CLASS(namespace, classname) // in the MsXpS namespace right after the corresponding class // declaration(header file). // // For example: // // } // namespace libXpertMassGui // MSXPS_REGISTER_JS_CLASS(MsXpS::MassXpert, ProgramWindow) // } // namespace MsXpS MsXpS::registerJsConstructorForEachClassInRegistrarMap(selected_js_engine_p); qDebug() << "Exiting function."; } void JavaScriptingEnvironment::registerOwnJsEntities(QJSEngine *js_engine_p) const { QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p == nullptr) qFatal() << "No JavaScript engine is currently available."; qDebug() << "Registering MsXpS::libXpertMassCore entities."; MsXpS::libXpertMassCore::registerEnumsToQJSEngine(selected_js_engine_p); MsXpS::libXpertMassCore::registerGlobalsToQJSEngine(selected_js_engine_p); qDebug() << "Done registering MsXpS::libXpertMassCore entities."; qDebug() << "Registering MsXpS::libXpertMassGui entities."; MsXpS::libXpertMassGui::registerEnumsToQJSEngine(selected_js_engine_p); MsXpS::libXpertMassGui::registerGlobalsToQJSEngine(selected_js_engine_p); qDebug() << "Done registering MsXpS::libXpertMassGui entities."; qDebug() << "Now returning from JavaScriptingEnvironment::registerOwnJsEntities()."; } void JavaScriptingEnvironment::registerExternalJsEntities( const QString &name_space, QJSEngine *js_engine_p) const { QJSEngine *selected_js_engine_p = js_engine_p; if(selected_js_engine_p == nullptr) selected_js_engine_p = mp_jsEngine; if(selected_js_engine_p == nullptr) qFatal() << "No JavaScript engine is currently available."; qDebug() << "Registering pappso:: entities."; if(name_space == "pappsomspp") pappso::registerEnumsToQJSEngine(selected_js_engine_p); qDebug() << "Done registering pappso:: entities."; qDebug() << "Now returning from " "JavaScriptingEnvironment::registerExternalJsEntities()."; } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptingGuiUtils.cpp000664 001750 001750 00000006730 15100504560 027063 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes /////////////////////// #include #include #include #include #include /////////////////////// libXpertMassCore includes /////////////////////// libXpertMassGUI includes ///////////////////////////// Local includes #include "MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingGuiUtils.hpp" #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" namespace MsXpS { namespace libXpertMassGui { JavaScriptingGuiUtils::JavaScriptingGuiUtils(QWidget *parent) : QObject(parent) { mp_javaScriptingWnd = dynamic_cast(parent); } JavaScriptingGuiUtils::~JavaScriptingGuiUtils() { } // This function serves as a js print replacement such that the output of the // print function goes to the outTextEdit and not to the system shell console. void JavaScriptingGuiUtils::print(const QString &text) { // Get to know the arguments to the jsPrint command, for example: // for (var property in ticChromPlot0) //{ // if (ticChromPlot0.hasOwnProperty(property)) //{ // jsPrint("property:" + ticChromPlot0[property]) //} //} if(mp_javaScriptingWnd == nullptr) qFatalStream() << "Programming error. The pointer cannot be nullptr."; mp_javaScriptingWnd->logOutTextEdit(text + "\n"); } // This function serves as a js print replacement such that the output of the // print function goes to the provided file nameand not to the system shell // console. void JavaScriptingGuiUtils::printToFile(const QString &text, const QString &file_name) { QFile file(file_name); if(!file.open(QIODevice::WriteOnly | QIODevice::Append)) { static_cast(mp_javaScriptingWnd) ->logOutTextEdit(QString("Failed to open file %1.\n").arg(file_name)); } QTextStream stream(&file); stream << text; stream << "\n"; stream.flush(); file.close(); } void JavaScriptingGuiUtils::msleep(int milliseconds) const { QThread::msleep(milliseconds); } void JavaScriptingGuiUtils::sleep(int seconds) const { QThread::sleep(seconds); } void JavaScriptingGuiUtils::processEvents() { QCoreApplication::processEvents(); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/JavaScriptingWnd.cpp000664 001750 001750 00000165573 15100504560 026061 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassGui/JavaScriptingWnd.hpp" #include "MsXpS/libXpertMassGui/JavaScriptWorker.hpp" #include "MsXpS/libXpertMassGui/ScriptingHistoryListWidget.hpp" #include "MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp" #include "ui_JavaScriptingWnd.h" namespace MsXpS { namespace libXpertMassGui { JavaScriptingWnd::JavaScriptingWnd(const QString &application_name, QMainWindow *parent) : QMainWindow(parent), m_applicationName(application_name), mp_javaScriptingGuiUtils(new JavaScriptingGuiUtils(this)), mp_scriptingEnvironment(new JavaScriptingEnvironment(this)), mp_ui(new ::Ui::JavaScriptingWnd) { // qDebug(); if(parent == nullptr) qFatal() << "Programming error. Pointer cannot be nullptr."; mp_ui->setupUi(this); initialize(); } JavaScriptingWnd::~JavaScriptingWnd() { // qDebug() << "Destructing..."; writeSettings(); } void JavaScriptingWnd::closeEvent(QCloseEvent *event) { qDebug() << "Closing..."; writeSettings(); event->accept(); } void JavaScriptingWnd::readSettings() { QSettings settings; settings.beginGroup("JavaScriptingWnd"); restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("windowState").toByteArray()); // Colors uint tempColorUint; tempColorUint = settings.value("successColor").toUInt(); if(tempColorUint != 0) m_successColor = settings.value("successColor").value(); m_logTextColorMap[LogTextColor::Success] = m_successColor; tempColorUint = settings.value("commentColor").toUInt(); if(tempColorUint != 0) m_commentColor = settings.value("commentColor").value(); m_logTextColorMap[LogTextColor::Comment] = m_commentColor; tempColorUint = settings.value("undefinedColor").toUInt(); if(tempColorUint != 0) m_undefinedColor = settings.value("undefinedColor").value(); m_logTextColorMap[LogTextColor::Undefined] = m_undefinedColor; tempColorUint = settings.value("failureColor").toUInt(); if(tempColorUint != 0) m_failureColor = settings.value("failureColor").value(); m_logTextColorMap[LogTextColor::Failure] = m_failureColor; tempColorUint = settings.value("jsInputColor").toUInt(); if(tempColorUint != 0) m_jsInputColor = settings.value("jsInputColor").value(); m_logTextColorMap[LogTextColor::JsInput] = m_jsInputColor; #if 0 QMapIterator i(m_logTextColorMap); while (i.hasNext()) { i.next(); qDebug() << __FILE__ << __LINE__ << i.key() << ": " << i.value() << endl; } #endif // Splitters mp_ui->treeSplitter->restoreState( settings.value("treeSplitter").toByteArray()); mp_ui->scriptingSplitter->restoreState( settings.value("scriptingSplitter").toByteArray()); mp_ui->treeSplitter->restoreState( settings.value("historySplitter").toByteArray()); mp_ui->generalConfigSplitter->restoreState( settings.value("generalConfigSplitter").toByteArray()); mp_ui->scriptingConfigSplitter->restoreState( settings.value("scriptingConfigSplitter").toByteArray()); // History restoreHistory(settings); // Set the index to outbound size, so that upon starting the dialog the // inTextWidget is blank and if the user explores the history by hitting // C-Up, then the last history item shows. // Also, set the anchor to that value. If the user then moves the keys in // the input text edit with Ctrl and Shift, then selection start at the // present index with anchor at this same index. m_lastHistoryIndex = m_historyAnchorIndex = mp_ui->historyListWidget->count(); m_historyLoggingType = static_cast(restoreHistoryLoggingType(settings)); bool wasVisible = settings.value("visible").toBool(); setVisible(wasVisible); settings.endGroup(); } void JavaScriptingWnd::writeSettings() { QSettings settings; settings.beginGroup("JavaScriptingWnd"); settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState()); // Colors // qDebug() << __FILE__ << __LINE__ //<< "Setting m_successColor:" << m_successColor; settings.setValue("successColor", m_successColor); settings.setValue("undefinedColor", m_undefinedColor); settings.setValue("failureColor", m_failureColor); settings.setValue("commentColor", m_commentColor); settings.setValue("jsInputColor", m_jsInputColor); // Splitters settings.setValue("treeSplitter", mp_ui->treeSplitter->saveState()); settings.setValue("historySplitter", mp_ui->treeSplitter->saveState()); settings.setValue("scriptingSplitter", mp_ui->scriptingSplitter->saveState()); settings.setValue("generalConfigSplitter", mp_ui->generalConfigSplitter->saveState()); settings.setValue("scriptingConfigSplitter", mp_ui->scriptingConfigSplitter->saveState()); // History settings.setValue("history", historyAsStringList(true)); settings.setValue("historyLoggingType", m_historyLoggingType); // Visibility settings.setValue("visible", isVisible()); settings.endGroup(); settings.sync(); } void JavaScriptingWnd::initializeJsEngine(QJSEngine *js_engine_p) { Q_ASSERT(js_engine_p != nullptr); Q_ASSERT(mp_javaScriptingGuiUtils != nullptr); // We want to provide the user with print() and printToFile() functions that // are not available in ECMAScript. These functions are defined in the // ScriptingGuiUtils class. QJSValue returned_js_value; mp_scriptingEnvironment->exposeQObject( js_engine_p, "JavaScriptingGuiUtils", "", /*alias*/ "The JavaScriptingGuiUtils provide utility functions", mp_javaScriptingGuiUtils, nullptr, returned_js_value, QJSEngine::ObjectOwnership::CppOwnership); js_engine_p->globalObject().setProperty("print", returned_js_value.property("print")); js_engine_p->globalObject().setProperty( "printToFile", returned_js_value.property("printToFile")); js_engine_p->globalObject().setProperty("msleep", returned_js_value.property("msleep")); js_engine_p->globalObject().setProperty("sleep", returned_js_value.property("sleep")); js_engine_p->globalObject().setProperty( "processEvents", returned_js_value.property("processEvents")); } void JavaScriptingWnd::initialize() { connect(mp_ui->historyListWidget, &ScriptingHistoryListWidget::setSelectedItemsTextToScriptInput, this, &JavaScriptingWnd::codeTextToCodeEditor); mp_codeEditor = new QsciScintilla(this); QsciLexerJavaScript *lexer = new QsciLexerJavaScript(mp_codeEditor); mp_codeEditor->setLexer(lexer); // Additional mp_codeEditor settings mp_codeEditor->setAutoIndent(true); mp_codeEditor->setIndentationGuides(true); mp_codeEditor->setMarginLineNumbers(1, true); mp_codeEditor->setMarginWidth(1, "0000"); mp_codeEditor->setBraceMatching(QsciScintilla::SloppyBraceMatch); // Set font QFont font("Consolas", 10); mp_codeEditor->setFont(font); lexer->setFont(font); mp_ui->qsciVerticalLayout->addWidget(mp_codeEditor); // This dialog window may be used by more than a single application, thus // set the app name along with the title. this->setWindowTitle( QString("%1 - Scripting console").arg(m_applicationName)); connect(mp_ui->resetHistoryPushButton, &QPushButton::released, this, &JavaScriptingWnd::resetHistory); connect(mp_ui->saveHistoryPushButton, &QPushButton::released, this, &JavaScriptingWnd::saveHistory); connect(mp_ui->historyListWidget, &QListWidget::itemActivated, this, &JavaScriptingWnd::historyListWidgetItemActivated); connect(mp_ui->historyListWidget, &ScriptingHistoryListWidget::currentRowChanged, this, &JavaScriptingWnd::historyListWidgetCurrentRowChanged); connect(mp_ui->historyRegExpLineEdit, &QLineEdit::returnPressed, this, &JavaScriptingWnd::historyRegExpLineEditReturnPressed); connect(mp_ui->jsRefSearchLineEdit, &QLineEdit::returnPressed, this, &JavaScriptingWnd::jsRefTextSearchLineEditReturnPressed); connect(mp_ui->logOnSuccessCheckBox, &QCheckBox::checkStateChanged, this, &JavaScriptingWnd::historyLoggingCheckBoxStateChanged); connect(mp_ui->logOnFailureCheckBox, &QCheckBox::checkStateChanged, this, &JavaScriptingWnd::historyLoggingCheckBoxStateChanged); mp_ui->historyListWidget->setSelectionMode( QAbstractItemView::ExtendedSelection); mp_ui->historyListWidget->setVerticalScrollMode( QAbstractItemView::ScrollPerItem); // Create the menu and menu items. // File menu mp_fileMenu = menuBar()->addMenu("&File"); mp_loadScriptFileAct = new QAction( tr("&Load a JavaScript file in the editor"), dynamic_cast(this)); mp_loadScriptFileAct->setShortcut(QKeySequence("Ctrl+L, F")); mp_loadScriptFileAct->setStatusTip( tr("Load a JavaScript file in the editor")); connect( mp_loadScriptFileAct, SIGNAL(triggered()), this, SLOT(loadScriptFile())); mp_fileMenu->addAction(mp_loadScriptFileAct); /// We'll need these strings many times. QString result; QString help; mp_ui->jsReferenceTextTabWidget->setMovable(true); // Register the JS reference texts for this // very same library's projects: libXpertMassCore and libXpertMassGui.' registerKnownJsReferenceTexts(); // This actually displays the JS reference texts previously registered above. displayAllMappedJsRefTextsToTabs(); initializeJsEngine(mp_scriptingEnvironment->getJsEngine()); show(); readSettings(); } JavaScriptingEnvironment * JavaScriptingWnd::getScriptingEnvironment() { return mp_scriptingEnvironment; } void JavaScriptingWnd::show() { QMainWindow::show(); } void JavaScriptingWnd::hide() { QMainWindow::hide(); } void JavaScriptingWnd::registerKnownJsReferenceTexts() { // The JS reference text exist as two files in the doc/js_reference directory // and are made available as Qt resources. #if 0 // Debugging code to list all the resources visible from here. QDir resourceDir(":/"); QStringList allFiles = resourceDir.entryList(QDir::Files | QDir::NoDotAndDotDot); QStringList allDirs = resourceDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); qDebug() << "Root resource files:" << allFiles; qDebug() << "Root resource directories:" << allDirs; // List subdirectories too for (const QString &dir : allDirs) { QDir subDir(":/" + dir); qDebug() << "Files in" << dir << ":" << subDir.entryList(QDir::Files); } #endif //////////////////// pappsomspp core and widget //////////////////// QFile jsClassRefPappsomsppCoreTextFile( ":/pappsomspp_core/class_js_reference_text.txt"); QString jsClassRefPappsomsppCoreText; if(!jsClassRefPappsomsppCoreTextFile.open(QFile::ReadOnly | QFile::Text)) { qCritical() << "Attention JavaScript class reference text not available for " "libpappsomspp Core:" << jsClassRefPappsomsppCoreTextFile.errorString(); } else { jsClassRefPappsomsppCoreText = jsClassRefPappsomsppCoreTextFile.readAll(); m_jsRefLabelRefTextMap["libPappsomsppCore"] = jsClassRefPappsomsppCoreText; qDebug() << "The libPappsomsppCore reference text has size" << m_jsRefLabelRefTextMap.value("libPappsomsppCore").size(); } // Note that this one will only be available when the program that uses this // JavaScriptingWnd class actually links to libpappsomspp-widget (MineXpert // but not MassXpert, for example). QFile jsClassRefPappsomsppGuiTextFile( ":/pappsomspp_gui/class_js_reference_text.txt"); QString jsClassRefPappsomsppGuiText; if(!jsClassRefPappsomsppGuiTextFile.open(QFile::ReadOnly | QFile::Text)) { qCritical() << "Attention JavaScript class reference text not available for " "libpappsomspp Gui:" << jsClassRefPappsomsppGuiTextFile.errorString(); } else { jsClassRefPappsomsppGuiText = jsClassRefPappsomsppGuiTextFile.readAll(); m_jsRefLabelRefTextMap["libPappsomsppGui"] = jsClassRefPappsomsppGuiText; qDebug() << "The libPappsomsppGui reference text has size" << m_jsRefLabelRefTextMap.value("libPappsomsppGui").size(); } //////////////////// libXpertMass and libXpertMassGui //////////////////// QFile jsClassRefXpertMassTextFile(":/core/class_js_reference_text.txt"); QString jsClassRefXpertMassText; if(!jsClassRefXpertMassTextFile.open(QFile::ReadOnly | QFile::Text)) { qCritical() << "Attention JavaScript class reference text not available for " "libXpertMassCore:" << jsClassRefXpertMassTextFile.errorString(); } else { jsClassRefXpertMassText = jsClassRefXpertMassTextFile.readAll(); m_jsRefLabelRefTextMap["libXpertMassCore"] = jsClassRefXpertMassText; qDebug() << "The libXpertMass reference text has size" << m_jsRefLabelRefTextMap.value("libXpertMass").size(); } QFile jsClassRefXpertMassGuiTextFile(":/gui/class_js_reference_text.txt"); QString jsClassRefXpertMassGuiText; if(!jsClassRefXpertMassGuiTextFile.open(QFile::ReadOnly | QFile::Text)) { qCritical() << "Attention JavaScript class reference text not available for " "libXpertMassGui:" << jsClassRefXpertMassGuiTextFile.errorString(); } else { jsClassRefXpertMassGuiText = jsClassRefXpertMassGuiTextFile.readAll(); m_jsRefLabelRefTextMap["libXpertMassGui"] = jsClassRefXpertMassGuiText; qDebug() << "The libXpertMassGui reference text has size" << m_jsRefLabelRefTextMap.value("libXpertMassGui").size(); } } void JavaScriptingWnd::registerJsReferenceText(const QString &label, const QString &text) { if(m_jsRefLabelRefTextMap.contains(label)) qWarning() << "A map entry with same" << label << "key is there already. Erasing it."; m_jsRefLabelRefTextMap[label] = text; } void JavaScriptingWnd::displayMappedJsRefTextToTab(const QString &label) { // The m_jsRefLabelRefTextMap relates a label, like "libXpertMass" to // a JS reference text to be displayed in its own Tab of the QTabWidget. if(!m_jsRefLabelRefTextMap.contains(label)) { qCritical() << "The map does not contain any such key:" << label << "Please first addLabelledJsReferenceText()."; return; } QTabWidget *js_reference_text_tab_widget_p = mp_ui->jsReferenceTextTabWidget; qDebug() << "The js reference tab widget has " << js_reference_text_tab_widget_p->count() << "pages"; QWidget *new_widget_p = new QWidget(); QVBoxLayout *page_layout_p = new QVBoxLayout(new_widget_p); page_layout_p->setContentsMargins(0, 0, 0, 0); QPlainTextEdit *text_edit_widget_p = new QPlainTextEdit(); text_edit_widget_p->setObjectName("plain_text_edit"); text_edit_widget_p->setPlainText(m_jsRefLabelRefTextMap.value(label)); page_layout_p->addWidget(new_widget_p); js_reference_text_tab_widget_p->addTab(new_widget_p, label); qDebug() << "The js reference tab widget has now" << js_reference_text_tab_widget_p->count() << "pages"; } void JavaScriptingWnd::displayAllMappedJsRefTextsToTabs() { // The m_jsRefLabelRefTextMap relates a label, like "libXpertMass" to // a JS reference text to be displayed in its own Tab of the QTabWidget. QTabWidget *js_reference_text_tab_widget_p = mp_ui->jsReferenceTextTabWidget; qDebug() << "The js reference tab widget has " << js_reference_text_tab_widget_p->count() << "pages"; foreach(const QString &label, m_jsRefLabelRefTextMap.keys()) { QWidget *new_widget_p = new QWidget(); QVBoxLayout *page_layout_p = new QVBoxLayout(new_widget_p); page_layout_p->setContentsMargins(0, 0, 0, 0); QPlainTextEdit *text_edit_widget_p = new QPlainTextEdit(); text_edit_widget_p->setObjectName("plain_text_edit"); text_edit_widget_p->setPlainText(m_jsRefLabelRefTextMap.value(label)); page_layout_p->addWidget(text_edit_widget_p); js_reference_text_tab_widget_p->addTab(new_widget_p, label); } qDebug() << "The js reference tab widget has now" << js_reference_text_tab_widget_p->count() << "pages"; } void JavaScriptingWnd::historyListWidgetCurrentRowChanged(int row) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ //<< "row is " << row << "; set index to that value"; m_lastHistoryIndex = row; } void JavaScriptingWnd::codeTextToCodeEditor(const QString code_text, bool overwrite) { if(overwrite) { mp_codeEditor->clear(); mp_codeEditor->setText(code_text); } else { mp_codeEditor->insert(code_text); } } void JavaScriptingWnd::historyListWidgetItemActivated() { // We need to get a string of all the currently selected items, and then // set that string to the inTextEdit widget in overwrite mode (true). QString code_text = mp_ui->historyListWidget->selectedItemsText(); Qt::KeyboardModifiers modifiers = QGuiApplication::queryKeyboardModifiers(); // If the user presses shift, than mean they do not want to replace // the contents in the script editor, but append new content. bool overwrite = !(modifiers & Qt::ShiftModifier); codeTextToCodeEditor(code_text, overwrite); } void JavaScriptingWnd::historyLoggingCheckBoxStateChanged() { // Rough work, iterate in the check boxes and construct a value with // their checkState HistoryLoggingType loggingType = HistoryLoggingType::Never; if(mp_ui->logOnSuccessCheckBox->isChecked()) loggingType = static_cast( loggingType | HistoryLoggingType::OnSuccess); if(mp_ui->logOnFailureCheckBox->isChecked()) loggingType = static_cast( loggingType | HistoryLoggingType::OnFailure); m_historyLoggingType = loggingType; } void JavaScriptingWnd::historyRegExpLineEditReturnPressed() { bool caseSensitive = mp_ui->historyCaseSensitiveCheckBox->isChecked(); QRegularExpression regExp(mp_ui->historyRegExpLineEdit->text(), (caseSensitive ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption)); for(int iter = 0; iter < mp_ui->historyListWidget->count(); ++iter) { QString itemText = mp_ui->historyListWidget->item(iter)->text(); QRegularExpressionMatch match = regExp.match(itemText); if(match.hasMatch()) { mp_ui->historyListWidget->item(iter)->setHidden(false); mp_ui->historyListWidget->setCurrentRow(iter, QItemSelectionModel::Select); } else mp_ui->historyListWidget->item(iter)->setHidden(true); } } int JavaScriptingWnd::regexFilterJsReferenceText(const QString &input_text, QString &filtered_text) { filtered_text.clear(); int contextLines = mp_ui->contextLinesSpinBox->value(); QString searched_text_expression = mp_ui->jsRefSearchLineEdit->text(); bool caseSensitive = mp_ui->jsRefCaseSensitiveCheckBox->isChecked(); QRegularExpression regExp(searched_text_expression, (caseSensitive ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption)); QStringList lines = input_text.split("\n"); for(int iter = 0; iter < lines.size(); ++iter) { // We may modify newIdx later, but if not, then it needs to be identical // to iter. int newIdx = iter; QRegularExpressionMatch match = regExp.match(lines.at(iter)); if(match.hasMatch()) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "match at iter:" << iter; // Handle the context lines insertion upstream of the matched line. // The position of the context line, either upstream or downstream of // the matched line. int pos = 0; if(contextLines > 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "Handling upstream text"; for(pos = contextLines; pos > 0; --pos) { newIdx = iter - pos; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "newIdx: " << newIdx; if(newIdx < 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << // "()" //<< "continue because newIdx less than 0"; continue; } filtered_text.append(lines.at(newIdx) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended " << lines.at(newIdx) << "for index" << newIdx; } } // Now that we have prepended the upstream context lines, actually // insert the matching line. filtered_text.append(lines.at(iter) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended matched line: " << lines.at(iter); // Handle the context lines insertion downstream of the matched line. if(contextLines > 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "Handling downstream text"; for(pos = 1; pos <= contextLines; ++pos) { newIdx = iter + pos; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "newIdx: " << newIdx; if(newIdx >= lines.size()) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << // "()" //<< "breaking because newIdx is greater or equal to // size()"; break; } filtered_text.append(lines.at(newIdx) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended " << lines.at(newIdx) << "for index" << newIdx; } } // Add match delimiting line. filtered_text.append("~~~~\n"); // Now, prepare next round in the lines string list at the new index + // 1 because we do not want to add matches within the // [-contextLines,+contextLines] text range. // iter will be incremented at next loop iteration. If no context line // was asked for, newIdx is equal to iter, so the following statement // does not change anything. If context lines were asked for, newIdx // is the index of the latest lines line appended to newText. iter = newIdx; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "after iter = newIdx, new iter: " << iter; } // End of // if(match.hasMatch()) } return filtered_text.size(); } int JavaScriptingWnd::verbatimFilterJsReferenceText(const QString &input_text, QString &filtered_text) { filtered_text.clear(); int contextLines = mp_ui->contextLinesSpinBox->value(); QString searched_text_expression = mp_ui->jsRefSearchLineEdit->text(); bool caseSensitive = mp_ui->jsRefCaseSensitiveCheckBox->isChecked(); Qt::CaseSensitivity case_sensitivity = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; QStringList lines = input_text.split("\n"); for(int iter = 0; iter < lines.size(); ++iter) { // We may modify newIdx later, but if not, then it needs to be identical // to iter. int newIdx = iter; if(lines.at(iter).contains(searched_text_expression, case_sensitivity)) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "match at iter:" << iter; // Handle the context lines insertion upstream of the matched line. // The position of the context line, either upstream or downstream of // the matched line. int pos = 0; if(contextLines > 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "Handling upstream text"; for(pos = contextLines; pos > 0; --pos) { newIdx = iter - pos; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "newIdx: " << newIdx; if(newIdx < 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << // "()" //<< "continue because newIdx less than 0"; continue; } filtered_text.append(lines.at(newIdx) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended " << lines.at(newIdx) << "for index" << newIdx; } } // Now that we have prepended the upstream context lines, actually // insert the matching line. filtered_text.append(lines.at(iter) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended matched line: " << lines.at(iter); // Handle the context lines insertion downstream of the matched line. if(contextLines > 0) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "Handling downstream text"; for(pos = 1; pos <= contextLines; ++pos) { newIdx = iter + pos; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "newIdx: " << newIdx; if(newIdx >= lines.size()) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << // "()" //<< "breaking because newIdx is greater or equal to // size()"; break; } filtered_text.append(lines.at(newIdx) + "\n"); // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "appended " << lines.at(newIdx) << "for index" << newIdx; } } // Add match delimiting line. filtered_text.append("~~~~\n"); // Now, prepare next round in the lines string list at the new index + // 1 because we do not want to add matches within the // [-contextLines,+contextLines] text range. // iter will be incremented at next loop iteration. If no context line // was asked for, newIdx is equal to iter, so the following statement // does not change anything. If context lines were asked for, newIdx // is the index of the latest lines line appended to newText. iter = newIdx; // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()" //<< "after iter = newIdx, new iter: " << iter; } // End of // if(match.hasMatch()) } return filtered_text.size(); } void JavaScriptingWnd::jsRefTextSearchLineEditReturnPressed() { // Start by defining which JS reference widget is currently shown. QWidget *current_widget_p = mp_ui->jsReferenceTextTabWidget->currentWidget(); QPlainTextEdit *plain_text_edit_widget_p = dynamic_cast( current_widget_p->findChild("plain_text_edit")); // qDebug() << "The text in the widget:" << // plain_text_edit_widget_p->toPlainText(); // What is the tab currently shown ? int tab_widget_page_index = mp_ui->jsReferenceTextTabWidget->indexOf(current_widget_p); QString tab_text = mp_ui->jsReferenceTextTabWidget->tabText(tab_widget_page_index); QString reference_text = m_jsRefLabelRefTextMap.value(tab_text); QString filtered_text; // If nothing in in the text search line edit widget, just display the // original JS reference text. if(mp_ui->jsRefSearchLineEdit->text().isEmpty()) { plain_text_edit_widget_p->setPlainText(reference_text); return; } if(mp_ui->verbatimCheckBox->isChecked()) verbatimFilterJsReferenceText(reference_text, filtered_text); else regexFilterJsReferenceText(reference_text, filtered_text); if(filtered_text.isEmpty()) filtered_text = "No matches were found. Erase the regular expression and press the " "Return key."; plain_text_edit_widget_p->setPlainText(filtered_text); } // We want to filter the keyboard event so as to catch the arrow up/down // key codes because they allow going through the command line history. void JavaScriptingWnd::keyPressEvent(QKeyEvent *event) { // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ //<< "current index:" << m_lastHistoryIndex //<< "; item count:" << mp_ui->historyListWidget->count(); if(event == nullptr) return; if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { Qt::KeyboardModifiers modifiers = QGuiApplication::queryKeyboardModifiers(); if(modifiers & Qt::ControlModifier) { // The Ctrl key is pressed, the user want to actually run the // script in the code editor widget. runScript(mp_codeEditor->text()); event->accept(); } return; } if(event->key() == Qt::Key_Down || event->key() == Qt::Key_Up) { Qt::KeyboardModifiers modifiers = QGuiApplication::queryKeyboardModifiers(); if(modifiers & Qt::AltModifier) { // If there is no history, then nothing to hope. if(mp_ui->historyListWidget->count() == 0) { event->accept(); return; } if(event->key() == Qt::Key_Down) { // qdebug() << __file__ << __line__ //<< "key down with index:" << m_lastHistoryIndex //<< "while count is:" << mp_ui->historyListWidget->count() //<< "going to increment index by one."; // Key_Down, means that we are going one item more recently in the // history. That is, we are moving down in the history list widget // to // more recent entries, thus we increment the index by one: ++m_lastHistoryIndex; if(!(modifiers & Qt::ShiftModifier)) { // If Shift is not pressed, there is no ongoing selection, // that // is, the new index is the sole selected item in the history // list // widget. // Be careful not to go past the last history item! if(m_lastHistoryIndex >= mp_ui->historyListWidget->count()) { // qDebug() << __FILE__ << __LINE__ //<< "count is:" << mp_ui->historyListWidget->count() //<< "but index is now >= count"; // The user want to go past the last history item, that is // she // wants to get an empty line edit so as to enter a brand // new // command line. So clear the line edit and return true // (we // processed the key code). Note that we set // m_lastHistoryIndex = // mp_ui->historyListWidget->count(); which would be fatal // if we did // not care of that value when using it later. The idea is // that if // the user finally renounces entering a new command line, // but // strikes the C-Up to actually go back to the last item // of the // history, we just need to decrement m_lastHistoryIndex // by one to // point the the right mp_ui->historyListWidget item. // // We do not need to fear problems here, because we are // not // accessing the history list widget from this function in // this // present case: we just return. // Set the current item in the history list widget to the // last // item. Not a single item must be selected. m_lastHistoryIndex = mp_ui->historyListWidget->count(); mp_ui->historyListWidget->deselectAll(); mp_ui->historyListWidget->setCurrentRow( m_lastHistoryIndex - 1, QItemSelectionModel::Deselect); // qDebug() << __FILE__ << __LINE__ //<< "Deselected all the items and set current to the last // one."; // We need to set that last index value to count() here, // because // the call to setCurrentRow triggers updating of that // index to // the (m_lastHistoryIndex - 1) value (see signal // connection to // currentRowChanged). m_lastHistoryIndex = mp_ui->historyListWidget->count(); // qDebug() << __FILE__ << __LINE__ //<< "So the index is set to the count::" << // m_lastHistoryIndex; // Now set the new selection anchor to that same value for // later // use if the user starts a selection process by pressing // the // Shift key. m_historyAnchorIndex = m_lastHistoryIndex; // qDebug() << __FILE__ << __LINE__ //<< "Set the anchor index to the same index value:" << // m_historyAnchorIndex; mp_codeEditor->clear(); // qDebug() << __FILE__ << __LINE__ //<< "Finally, cleared the inTextEdit"; event->accept(); return; } else { // We are not past the end of the history item list, all // is // fine, simply selecting the item at m_lastHistoryIndex. // qDebug() << __FILE__ << __LINE__ //<< "Should be selecting the single item at index " << // m_lastHistoryIndex; mp_ui->historyListWidget->item(m_lastHistoryIndex) ->setSelected(true); // Also set the anchor index to the same value. m_historyAnchorIndex = m_lastHistoryIndex; // qDebug() << __FILE__ << __LINE__ //<< "Set the anchor index to the same index value:" << // m_historyAnchorIndex; } } else // the shift key modifier was pressed { // If Shift is pressed, the user is willing to make a // selection. // The Key_Down moves the history down one element. But // imagine the // user was selecting upwards before this key, then we are // reducing // the selection. The anchor index value can help us. // The user has effectively selected something if the anchor // is // different than the current index. if(m_lastHistoryIndex >= mp_ui->historyListWidget->count()) { // qDebug() << __FILE__ << __LINE__ //<< "count is:" << mp_ui->historyListWidget->count() //<< "but index is now >= count" //<< "setting last index to count" //<< "not changing the anchro" //<< "should be selecting items from " << // m_historyAnchorIndex //<< "to " << m_lastHistoryIndex; m_lastHistoryIndex = mp_ui->historyListWidget->count(); } if(m_lastHistoryIndex != m_historyAnchorIndex) { // qDebug() << __FILE__ << __LINE__ //<< "We are selecting items between indices " << // m_lastHistoryIndex //<< "to " << m_historyAnchorIndex; } else { // qDebug() << __FILE__ << __LINE__ //<< "last index and anchor index have the same value:" << // m_lastHistoryIndex //<< ": should select the single item at // m_lastHistoryIndex"; } } // End of // else // the shift key modifier was pressed } // End of // if(event->key() == Qt::Key_Down) else if(event->key() == Qt::Key_Up) { // qDebug() << __FILE__ << __LINE__ //<< "key up with index: " << m_lastHistoryIndex //<< "while count is:" << mp_ui->historyListWidget->count() //<< "going to decrement index by one."; if(m_lastHistoryIndex > 0) { --m_lastHistoryIndex; // qDebug() << __FILE__ << __LINE__ //<< "decremented index by one to value: " << // m_lastHistoryIndex; } else { // qDebug() << __FILE__ << __LINE__ //<< "Not decremented, m_lastHistoryIndex is already == 0."; } if(!(modifiers & Qt::ShiftModifier)) { // If Shift is not pressed, there is no ongoing selection, // that // is, the new index is the sole selected item in the history // list // widget. if(m_lastHistoryIndex < 0) { // qDebug() << __FILE__ << __LINE__ //<< "but now index is less than 0, so set to 0" //<< "same for the anchor because we are not pressing // shift." //<< "The history list item at index 0 should thus be // selected."; m_lastHistoryIndex = 0; m_historyAnchorIndex = m_lastHistoryIndex; } else { // We are not past the top of the history item list, all // is // fine, simply selecting the item at m_lastHistoryIndex. // qDebug() << __FILE__ << __LINE__ //<< "Should be selecting the single item at index " << // m_lastHistoryIndex; // Also set the anchor index to the same value. m_historyAnchorIndex = m_lastHistoryIndex; // qDebug() << __FILE__ << __LINE__ //<< "Set the anchor index to the same index value:" << // m_historyAnchorIndex; } } else // the shift key modifier was pressed { // If Shift is pressed, the user is willing to make a // selection. // The Key_Up moves the history up element. But imagine the // user was selecting downwards before this key, then we are // reducing // the selection. The anchor index value can help us. // The user has effectively selected something if the anchor // is // different than the current index. if(m_lastHistoryIndex != m_historyAnchorIndex) { // qDebug() << __FILE__ << __LINE__ //<< "We are selecting items between indices " << // m_lastHistoryIndex //<< "to " << m_historyAnchorIndex; } else { // qDebug() << __FILE__ << __LINE__ //<< "last index and anchor index have the same value:" << // m_lastHistoryIndex //<< ": should select the single item at // m_lastHistoryIndex"; } } // End of // else // the shift key modifier was pressed } // End of // else if(event->key() == Qt::Key_Up) // At this point we have anchor and index, so we can select the // item(s): mp_ui->historyListWidget->selectItemIndices(m_lastHistoryIndex, m_historyAnchorIndex); // Now that the item(s) have been selected, let's get their text and // put that text into the input text edit. mp_ui->historyListWidget->overwriteSelectedItemsText(); // We did handle the event. event->accept(); return; } // End of // if(event->key() == Qt::Key_Down || event->key() == Qt::Key_Up) } // End of // if(modifiers & Qt::ControlModifier) // We did not handle the event. return; } QStringList JavaScriptingWnd::historyAsStringList(bool colorTagged) { // We want to create a string list, where each string is a history item // from the history list widget. But, if colorTagged is set to true, // we want to prepend to each such string a x tag // reflecting the color that was used to render the list widget item. This // way, we serialize also the color of the item, not only the text. QStringList historyList; int count = mp_ui->historyListWidget->count(); for(int iter = 0; iter < count; ++iter) { QListWidgetItem *item = mp_ui->historyListWidget->item(iter); QString itemText = item->text(); if(colorTagged) { QBrush brush = item->foreground(); QColor color = brush.color(); LogTextColor colorText = static_cast( m_logTextColorMap.key(color, LogTextColor::Comment)); prependColorTag(itemText, colorText); } historyList.append(itemText); } return historyList; } int JavaScriptingWnd::restoreHistory(QSettings &settings) { QStringList wholeHistoryAsListOfStrings = settings.value("history").toStringList(); qsizetype loaded_history_lines = 0; // qDebug() << "History has " << wholeHistoryAsListOfStrings.size() << // "lines"; QProgressDialog *progress_dialog_p = new QProgressDialog(this, Qt::Dialog); progress_dialog_p->setLabelText("Importing scripting history"); progress_dialog_p->setMaximum(wholeHistoryAsListOfStrings.size()); connect(progress_dialog_p, &QProgressDialog::canceled, [this]() { m_shouldHistoryLoadCancel = true; }); // When we store the list items, we store them with a xx // tag prepended to the text. We use that that to establish the color with // which to display the text in the history listwidget. for(int iter = 0; iter < wholeHistoryAsListOfStrings.size(); ++iter) { QString historyText = wholeHistoryAsListOfStrings.at(iter); if(historyText.isEmpty()) continue; LogTextColor color = LogTextColor::Comment; if(removeColorTag(historyText, color)) { // color now contains the color (call returned true). logHistory(historyText, color); } else { // The color will be the color of the comments. logHistory(historyText, color); } ++loaded_history_lines; progress_dialog_p->setValue(loaded_history_lines); if(m_shouldHistoryLoadCancel) break; } delete progress_dialog_p; // Make sure the history list widget shows the last item. mp_ui->historyListWidget->setCurrentRow(mp_ui->historyListWidget->count() - 1); // Now make sure that we set the last history item index to the size of // the history list. That is outbound, but we are not accessing that item, // never, always making sure we do not try to accessit. This should go // along the fact that the inTextEdit should be blank and the first C-Up // key strike by the user to explore history should print the last history // item fine. // Also, set the anchor to that value. If the user then moves the keys in // the input text edit with Ctrl and Shift, then selection start at the // present index with anchor at this same index. m_lastHistoryIndex = m_historyAnchorIndex = mp_ui->historyListWidget->count(); // qDebug() << __FILE__ << __LINE__ //<< "Finished restoring history with " << mp_ui->historyListWidget->count() //<< "items" //<< "set the index to" << m_lastHistoryIndex << "and the anchor at the same // value."; return m_lastHistoryIndex; } int JavaScriptingWnd::restoreHistoryLoggingType(QSettings &settings) { // The type of history logging. HistoryLoggingType historyLoggingType = static_cast( settings.value("historyLoggingType").toInt(), HistoryLoggingType::Always); if(historyLoggingType & HistoryLoggingType::OnSuccess) mp_ui->logOnSuccessCheckBox->setChecked(true); if(historyLoggingType & HistoryLoggingType::OnFailure) mp_ui->logOnFailureCheckBox->setChecked(true); return historyLoggingType; } void JavaScriptingWnd::resetHistory() { mp_ui->historyListWidget->clear(); m_lastHistoryIndex = m_historyAnchorIndex = -1; } void JavaScriptingWnd::saveHistory() { QSettings settings; settings.beginGroup("JavaScriptingWnd"); // History settings.setValue("history", historyAsStringList(true)); settings.setValue("historyLoggingType", m_historyLoggingType); settings.endGroup(); settings.sync(); } void JavaScriptingWnd::showHistory() { QList widgetList = mp_ui->tabWidget->findChildren("historyTab"); if(widgetList.isEmpty()) qFatal( "Fatal error at %s@%d -- %s(). " "Cannot be that no history tab be found." "Program aborted.", __FILE__, __LINE__, __FUNCTION__); mp_ui->tabWidget->setCurrentWidget(widgetList.first()); } QString JavaScriptingWnd::historyAsString(bool colorTagged, const QString &lineSeparator) { QStringList historyList = historyAsStringList(colorTagged); return historyList.join(lineSeparator); } void JavaScriptingWnd::loadScriptFile(const QString &fileName) { QString scriptFileName = fileName; if(scriptFileName.isEmpty()) { QFileDialog fileDialog(this); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setViewMode(QFileDialog::Detail); fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setDirectory(QDir::home()); QStringList fileNames; if(fileDialog.exec()) { scriptFileName = fileDialog.selectedFiles().first(); } else return; } QFile file(scriptFileName); if(!file.open(QFile::ReadOnly | QFile::Text)) return; QTextStream stream(&file); QString code_text = stream.readAll(); file.close(); if(code_text.isEmpty()) return; mp_codeEditor->setText(code_text); } QColor JavaScriptingWnd::logColor(LogTextColor contextColor) { QColor color = m_logTextColorMap.value(contextColor); // qDebug() << "Returning color" << color << "for contextColor:" << // contextColor; return color; } void JavaScriptingWnd::logOutTextEdit(const QString &text, LogTextColor textColor) { mp_ui->outTextEdit->moveCursor(QTextCursor::End); QTextCursor cursor(mp_ui->outTextEdit->textCursor()); QTextCharFormat format; QColor reqColor = logColor(textColor); format.setForeground(QBrush(reqColor)); cursor.setCharFormat(format); cursor.insertText(text); mp_ui->outTextEdit->verticalScrollBar()->setValue( mp_ui->outTextEdit->verticalScrollBar()->maximum()); } /*! \brief Outputs to the feedback text edit widget the text in the parameters. The text that is crafted and displayed aims at identifying a JavaScript property resulting from exposure of a QObject to the scripting engine. \list \li \a object_name: the name of the object as a JavaScript property name \li \a object_alas: another way to access that property \li \a object_description: descriptive text for the object exposed \endlist */ void JavaScriptingWnd::feedBackForExposedQObject(const QString &object_name, const QString &object_alias, const QString &object_description) { QTextCursor cursor = mp_ui->feedbackTextEdit->textCursor(); QTextCharFormat boldFormat; boldFormat.setFontWeight(QFont::Bold); QTextCharFormat normalFormat; boldFormat.setFontWeight(QFont::Normal); // Insert bold text cursor.insertText(object_name, boldFormat); if(!object_alias.isEmpty()) { cursor.insertText(" aliased as ", normalFormat); cursor.insertText(QString("%1").arg(object_alias), normalFormat); } // Insert normal text after cursor.insertText("\n"); cursor.insertText(object_description, normalFormat); cursor.insertText("\n\n"); } void JavaScriptingWnd::logHistory(const QString &text, LogTextColor textColor) { // qDebug() << "Logging history" << text << ", before insertion of new item" // << ", item count is:" << mp_ui->historyListWidget->count() // << "; with last index:" << m_lastHistoryIndex // << "with color:" << m_logTextColorMap[textColor]; // Make a copy because we'll modify it. QString localText = text; // Allocate a new item that is automatically set to the list widget. QListWidgetItem *item = new QListWidgetItem(mp_ui->historyListWidget); QFont font(item->font()); font.setPointSize(11); item->setFont(font); // Make sure the item is rendered according to the textColor. QColor color = logColor(textColor); // qDebug() << "We should be logging history text" << text << "with color:" << // color; item->setForeground(color); item->setText(text); // Since we just added a new item, we deselect all the history list widget // and set current item to the last one. mp_ui->historyListWidget->deselectAll(); // qDebug() << __FILE__ << __LINE__ //<< "Deselecting all items, setting current row to last item."; mp_ui->historyListWidget->setCurrentRow(m_lastHistoryIndex - 1, QItemSelectionModel::Deselect); // We need to set that last index value to count() here, because // the call to setCurrentRow triggers updating of that index to // the (m_lastHistoryIndex - 1) value (see signal connection to // currentRowChanged). // Also, set the anchor to that value. If the user then moves the keys in // the input text edit with Ctrl and Shift, then selection start at the // present index with anchor at this same index. m_lastHistoryIndex = m_historyAnchorIndex = mp_ui->historyListWidget->count(); // qDebug() << __FILE__ << __LINE__ //<< "logging history, after insertion of new item" //<< ", item count:" << mp_ui->historyListWidget->count() //<< "; with last index set to that count:" << m_lastHistoryIndex; // Make sure the last item is visible mp_ui->historyListWidget->setCurrentItem(item); } QString & JavaScriptingWnd::prependColorTag(QString &text, LogTextColor color) { // We get a string and a color rendering. Create a tag in the form // withthe color value inside and prepend that tag to the // text. text.prepend(QString("%1").arg(color)); return text; } bool JavaScriptingWnd::removeColorTag(QString &text, LogTextColor &color) { // We get a string that is prepended with the "%1" color // tag. We need to extrat the value between the opening and closing tags // an set it. In the same operation we remove that color tag from the // string. We return false if the color tag was not there or if the value // was not convertible to int. QRegularExpression colorTagRegexp("(\\d+)"); QRegularExpressionMatch match = colorTagRegexp.match(text); if(match.hasMatch()) { bool ok = false; // Get the color value int tagValue = match.captured(1).toInt(&ok); if(!ok) { // qDebug() << __FILE__ << __LINE__ << "Failed to convert" //<< match.captured(1) << "to int."; return false; } color = static_cast(tagValue); // Now remove from the initial string all the matching substring. text.remove(colorTagRegexp); return true; } return false; } void JavaScriptingWnd::runScript(const QString &code_text, const QString &comment) { qDebug() << "20251007-Running script"; // We want to run the script in its own thread. For this, the QJSEngine // that will evaluate the script NEEDS to have been allocated IN THAT THREAD. // We thus first allocate a JavaScriptWorker object, that will itself // allocate a QJSEngine object. From that JavaScriptWorker object, we // will need to initialize the the QJSEngine object in that other thread // and the only one object that knows how to do this is ProgramWindow. // This is why the function // ProgramWindow::initializeJsEngine(QJSEngine *engine_p). m_scriptText = code_text; m_scriptComment = comment; auto *java_script_worker_p = new JavaScriptWorker(code_text, mp_scriptingEnvironment); connect(java_script_worker_p, &JavaScriptWorker::finishedSignal, java_script_worker_p, &QObject::deleteLater); connect(java_script_worker_p, &JavaScriptWorker::finishedSignal, this, &JavaScriptingWnd::scriptHasFinishedRunning); // When the user clicks that push button, the value of __abortRequested // in the scriptiong environment is set to true. When checkAbort() is // called in the running script (provided the user thas put that call // in relevant parts of their script), the true value that is returned // should trigger relevant code to stop execution softly. connect(mp_ui->scriptAbortPushButton, &QPushButton::clicked, java_script_worker_p, &JavaScriptWorker::requestAbort); java_script_worker_p->run(); } void JavaScriptingWnd::scriptHasFinishedRunning(QJSValue value) { qDebug() << "20251007-The script has finished running."; // If there is an explanatory comment to this script command, then start // by logging it, so that it precedes the result output in the // logOutTextEdit widget. if(!m_scriptComment.isEmpty()) logOutTextEdit(QString("%1%2").arg(m_commentPrefix).arg(m_scriptComment), LogTextColor::Comment); QStringList script_as_lines = m_scriptText.split("\n"); bool scriptFailed = value.isError(); if(scriptFailed) { // red color. logOutTextEdit(value.toString() + "\n", LogTextColor::Failure); if(m_historyLoggingType & HistoryLoggingType::OnFailure) { foreach(QString line, script_as_lines) { logHistory(line, LogTextColor::Failure); } } } else if(value.isUndefined()) { // black color; logOutTextEdit(value.toString() + "\n", LogTextColor::Undefined); if(m_historyLoggingType & HistoryLoggingType::OnSuccess) { foreach(QString line, script_as_lines) { logHistory(line, LogTextColor::Undefined); } } } else { // green color. logOutTextEdit(value.toString() + "\n", LogTextColor::Success); if(m_historyLoggingType & HistoryLoggingType::OnSuccess) { foreach(QString line, script_as_lines) { logHistory(line, LogTextColor::Success); } } } qDebug() << "20251007-The result is:" << value.toString(); QString error_details; if(value.isError()) { error_details = QString("Name: %1\nMessage: %2\nStack: %3\n") .arg(value.property("name").toString()) .arg(value.property("message").toString()) .arg(value.property("stack").toString()); qDebug() << "Error details:" << error_details; } // When a command has been run, whatever its result, we remove it from the // text edit widget. Anyway, it has been already taken into account in the // history by the logHistory() call above. mp_codeEditor->clear(); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/MassDataClientServerConfigDlg.cpp000664 001750 001750 00000026302 15100504560 030426 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Std lib includes /////////////////////// Qt includes #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassGui/MassDataClientServerConfigDlg.hpp" #include "ui_MassDataClientServerConfigDlg.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::MassDataClientServerConfigDlg \inmodule libXpertMassGui \ingroup XpertMassGuiUtilities \inheaderfile MassDataClientServerConfigDlg.hpp \brief The MassDataClientServerConfigDlg class provides a mass data client-server network connection configuration dialog window. */ /*! \variable MsXpS::libXpertMassGui::MassDataClientServerConfigDlg::mp_ui \brief The graphical user interface definition. */ /*! \variable \ MsXpS::libXpertMassGui::MassDataClientServerConfigDlg::m_applicationName \brief The name of the application. */ /*! \variable \ MsXpS::libXpertMassGui::MassDataClientServerConfigDlg::mp_programWindow \brief The main program window. */ /*! \brief Constructs a MassDataClientServerConfigDlg instance. \list \li \a program_window_p: the program's main window. \li \a applicationName: the name of the application, typically massXpert3 or mineXpert3, for example. \li \a description: the string describing what this dialog window is for (used for the window title). \li \a server_ip_config: a pair of values, IP address as a string and port number as an integer. \endlist */ MassDataClientServerConfigDlg::MassDataClientServerConfigDlg( QWidget *program_window_p, const QString &applicationName, const QString &description, std::pair server_ip_config) : QDialog(program_window_p), m_applicationName{applicationName}, mp_programWindow(program_window_p), m_serverIpConfig(server_ip_config), mp_ui(new Ui::MassDataClientServerConfigDlg) { if(!program_window_p) qFatal("Programming error."); mp_ui->setupUi(this); // Update the window title because the window title element in mp_ui->might be // either erroneous or empty. setWindowTitle(QString("%1 - %2").arg(applicationName).arg(description)); //// We want to destroy the dialog when it is closed. // setAttribute(Qt::WA_DeleteOnClose); if(!server_ip_config.first.isEmpty()) mp_ui->localServerIpAddressLineEdit->setText(server_ip_config.first); else if(!m_serverIpConfig.first.isEmpty()) mp_ui->localServerIpAddressLineEdit->setText(m_serverIpConfig.first); if(server_ip_config.second != 0) mp_ui->localServerPortNumberLineEdit->setText( QString("%1").arg(server_ip_config.second)); else if(m_serverIpConfig.second != 0) mp_ui->localServerPortNumberLineEdit->setText( QString("%1").arg(m_serverIpConfig.second)); // Perform all the connections. // Typically, the user of startServerSignal is ProgramWindow that created this // dialog window. connect(mp_ui->startServerPushButton, &QPushButton::clicked, this, [this]() { emit startServerSignal(); }); // Typically, the user of stopServerSignal is ProgramWindow that created this connect(mp_ui->stopServerPushButton, &QPushButton::clicked, this, [this]() { emit stopServerSignal(); }); connect(mp_ui->startClientPushButton, &QPushButton::clicked, this, &MassDataClientServerConfigDlg::startClient); connect(mp_ui->stopClientPushButton, &QPushButton::clicked, this, [this]() { emit stopClientSignal(); }); connect(mp_ui->clearRemoteIpAddressPushButton, &QPushButton::clicked, this, [this]() { mp_ui->remoteServerIpAddressComboBox->removeItem( mp_ui->remoteServerIpAddressComboBox->currentIndex()); }); connect(mp_ui->clearRemotePortNumberPushButton, &QPushButton::clicked, this, [this]() { mp_ui->remoteServerPortNumberComboBox->removeItem( mp_ui->remoteServerPortNumberComboBox->currentIndex()); }); readSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Destructs this MassDataClientServerConfigDlg instance. */ MassDataClientServerConfigDlg::~MassDataClientServerConfigDlg() { writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Does not close the window, just hides it (\a event is not used). */ void MassDataClientServerConfigDlg::closeEvent([[maybe_unused]] QCloseEvent *event) { qDebug(); hide(); } /*! \brief Writes the settings of the dialog window to \a config_settings_file_path for later restoration. */ void MassDataClientServerConfigDlg::writeSettings( const QString &config_settings_file_path) { // qDebug(); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassDataClientServerConfigDlg"); settings.setValue("geometry", saveGeometry()); // Save the remove server IP history list from the combo box. QStringList items; for(int iter = 0; iter < mp_ui->remoteServerIpAddressComboBox->count(); ++iter) items.append(mp_ui->remoteServerIpAddressComboBox->itemText(iter)); settings.setValue("remote_server_ip_addresses", items); items.clear(); for(int iter = 0; iter < mp_ui->remoteServerPortNumberComboBox->count(); ++iter) items.append(mp_ui->remoteServerPortNumberComboBox->itemText(iter)); settings.setValue("remote_server_port_numbers", items); // Retrieve QStringList QStringList retrievedList = settings.value("myList").toStringList(); settings.endGroup(); } /*! \brief Reads the settings of this dialog window from \a config_settings_file_path. */ void MassDataClientServerConfigDlg::readSettings( const QString &config_settings_file_path) { // qDebug(); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassDataClientServerConfigDlg"); restoreGeometry(settings.value("geometry").toByteArray()); // Retrieve the IP addresses QStringList items = settings.value("remote_server_ip_addresses").toStringList(); if(items.size()) { foreach(QString item, items) mp_ui->remoteServerIpAddressComboBox->addItem(item); mp_ui->remoteServerIpAddressComboBox->setCurrentText(items.last()); items.clear(); } // Retrieve the port numbers items = settings.value("remote_server_port_numbers").toStringList(); if(items.size()) { foreach(QString item, items) mp_ui->remoteServerPortNumberComboBox->addItem(item); mp_ui->remoteServerPortNumberComboBox->setCurrentText(items.last()); } settings.endGroup(); } void MassDataClientServerConfigDlg::updateServerIpData( std::pair server_ip_config) { m_serverIpConfig = server_ip_config; mp_ui->localServerIpAddressLineEdit->setText(m_serverIpConfig.first); mp_ui->localServerPortNumberLineEdit->setText( QString("%1").arg(m_serverIpConfig.second)); } void MassDataClientServerConfigDlg::updateClientIpData( std::pair client_ip_config) { m_clientIpConfig = client_ip_config; mp_ui->remoteServerIpAddressComboBox->addItem(m_clientIpConfig.first); mp_ui->remoteServerPortNumberComboBox->addItem( QString("%1").arg(m_clientIpConfig.second)); } /*! \brief Starts the client using the configuration entered in the dialog window. */ void MassDataClientServerConfigDlg::startClient() { // We need to know what is the requested configuration. QString ip_address = mp_ui->remoteServerIpAddressComboBox->currentText(); if(ip_address.isEmpty()) { message("Please, configure the remote server IP address."); return; } QString port_number_string = mp_ui->remoteServerPortNumberComboBox->currentText(); if(port_number_string.isEmpty()) { message("Please, configure the remote server port number"); return; } bool ok = false; int port_number = port_number_string.toInt(&ok); if(!ok) { message( "Please, configure the remote server port number (that should be an " "integer)"); return; } m_clientIpConfig = std::pair(ip_address, port_number); qDebug() << "Asking that a client be started to connect to a remote server:" << ip_address << ":" << port_number; // Typically the user of startClientSignal is ProgramWindow that created this // dialog window. emit startClientSignal(ip_address, port_number); } /*! \brief Writes the \a message to the message line edit widget. After \a timeout milliseconds, the message is erased. By default that timeout is 3000 ms. */ void MassDataClientServerConfigDlg::message(const QString &message, int timeout) { if(mp_ui->messageLineEdit->text() == message) return; mp_ui->messageLineEdit->setText(message); QTimer::singleShot(timeout, [this]() { mp_ui->messageLineEdit->setText(""); }); } /*! \brief Update the client configuration on the basis of \a ip_address and \a port_number. This is the configuration of the server to which this client will connect. */ void MassDataClientServerConfigDlg::updateClientConfigurationData( const QString &ip_address, int port_number) { mp_ui->localServerIpAddressLineEdit->setText(ip_address); mp_ui->localServerPortNumberLineEdit->setText(QString::number(port_number)); } /*! \brief Writes the \a error to the message line edit widget. After a imeout of 10 seconds, the message is erased. */ void MassDataClientServerConfigDlg::clientFailingFeedback(const QString &error) { qDebug(); message(error, 10000); } void MassDataClientServerConfigDlg::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register MassDataClientServerConfigDlg class: " "engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&MassDataClientServerConfigDlg::staticMetaObject); engine->globalObject().setProperty("MassDataClientServerConfigDlg", jsMetaObject); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/MassPeakShaperConfigDlg.cpp000664 001750 001750 00000020673 15100504560 027257 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include // for std::numeric_limits /////////////////////// Qt includes #include #include /////////////////////// pappsomspp includes #include #include #include #include #include /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/MassDataCborMassSpectrumHandler.hpp" #include "MsXpS/libXpertMassGui/MassPeakShaperConfigDlg.hpp" #include "ui_MassPeakShaperConfigDlg.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::MassPeakShaperConfigDlg \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile MassPeakShaperConfigDlg.hpp \brief The MassPeakShaperConfigDlg class provides a graphical user interface for the configuration of the mass peak shaping process. The dialog window contains all the widgets required to fully configure the mass peak shaping process. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigDlg::mp_ui \brief The graphical user interface definition. */ /*! \variable \ MsXpS::libXpertMassGui::MassPeakShaperConfigDlg::mp_massPeakShaperConfigWidget \brief The widget holding the configuration of the mass peak shaping procedure. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigDlg::mp_parent \brief The parent widget. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigDlg::m_applicationName \brief The name of the application. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigDlg::m_config \brief The \l{libXpertMassCore::MassPeakShaperConfig} configuration for the mass peak shaping process. */ /*! \brief Constructs a MassPeakShaperConfigDlg instance. \list \li \a parent_p: the parent widget. \li \a applicationName: the name of the application, typically massXpert2 or mineXpert2, for example. \li \a description: the string describing what this dialog window is for (used for the window title). \endlist */ MassPeakShaperConfigDlg::MassPeakShaperConfigDlg(QWidget *parent_p, const QString &applicationName, const QString &description) : QDialog(static_cast(parent_p)), mp_ui(new Ui::MassPeakShaperConfigDlg), mp_parent(parent_p), m_applicationName{applicationName} { if(!parent_p) qFatal("Programming error. Program aborted."); mp_ui->setupUi(this); // Update the window title because the window title element in m_ui might be // either erroneous or empty. setWindowTitle(QString("%1 - %2").arg(applicationName).arg(description)); setupDialog(); } /*! \fn MassPeakShaperConfigDlg::closeEvent([[maybe_unused]] QCloseEvent *event) Upon closing of the dialog window writes the settings of the dialog windows (\a event is unused). \sa writeSettings() */ void MassPeakShaperConfigDlg::closeEvent([[maybe_unused]] QCloseEvent *event) { // qDebug(); writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Destructs this MassPeakShaperConfigDlg instance. */ MassPeakShaperConfigDlg::~MassPeakShaperConfigDlg() { // qDebug(); writeSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } /*! \brief Writes the settings of the dialog window to \a config_settings_file_path for later restoration. */ void MassPeakShaperConfigDlg::writeSettings(const QString &config_settings_file_path) { // qDebug(); // First save the config widget state. mp_massPeakShaperConfigWidget->writeSettings(config_settings_file_path); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassPeakShaperConfigDlg"); settings.setValue("geometry", saveGeometry()); settings.endGroup(); } /*! \brief Reads the settings of this dialog window from \a config_settings_file_path. */ void MassPeakShaperConfigDlg::readSettings(const QString &config_settings_file_path) { // qDebug(); // First read the config widget state. mp_massPeakShaperConfigWidget->readSettings(config_settings_file_path); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassPeakShaperConfigDlg"); restoreGeometry(settings.value("geometry").toByteArray()); settings.endGroup(); } /*! \brief Sets up this dialog window. */ void MassPeakShaperConfigDlg::setupDialog() { // We want to destroy the dialog when it is closed. // setAttribute(Qt::WA_DeleteOnClose); // Setup the peak shaper configuration widget inside of the frame QVBoxLayout *v_box_layout_p = new QVBoxLayout(this); // Trasmit the config member object reference so that it can be modified by // the configuration widget. mp_massPeakShaperConfigWidget = new MassPeakShaperConfigWidget(m_config, this); v_box_layout_p->addWidget(mp_massPeakShaperConfigWidget); mp_ui->peakShapeConfigFrame->setLayout(v_box_layout_p); connect(mp_massPeakShaperConfigWidget, &MassPeakShaperConfigWidget::updatedMassPeakShaperConfigSignal, [this](const libXpertMassCore::MassPeakShaperConfig &config) { m_config.initialize(config); qDebug().noquote() << "Now got the config:" << m_config.toString(); // Relay the config to the user of this dialog window. emit updatedMassPeakShaperConfigSignal(m_config); }); // The values above are actually set in readSettings (with default values if // missing in the config settings.) readSettings( libXpertMassCore::Utils::craftConfigSettingsFilePath(m_applicationName)); } void MassPeakShaperConfigDlg::setParameters( double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor, bool should_create_bins) { return mp_massPeakShaperConfigWidget->setParameters(reference_peak_mz, point_count, mass_peak_shape_type, resolution, fwhm, bin_size_divisor, should_create_bins); } MassPeakShaperConfigWidget * MassPeakShaperConfigDlg::getConfigWidget() { return mp_massPeakShaperConfigWidget; } void MassPeakShaperConfigDlg::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register MassPeakShaperConfigDlg class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&MassPeakShaperConfigDlg::staticMetaObject); engine->globalObject().setProperty("MassPeakShaperConfigDlg", jsMetaObject); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/MassPeakShaperConfigWidget.cpp000664 001750 001750 00000071564 15100504560 030001 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include // for std::numeric_limits /////////////////////// Qt includes #include #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassCore/Utils.hpp" #include "MsXpS/libXpertMassGui/MassPeakShaperConfigWidget.hpp" #include "ui_MassPeakShaperConfigWidget.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::MassPeakShaperConfigWidget \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile MassPeakShaperConfigWidget.hpp \brief The MassPeakShaperConfigWidget class provides a widget for the configuration of the mass peak shaping process. This composite widget contains all the widgets and all the logic required to fully configure the mass peak shaping process. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::mp_ui \brief The graphical user interface definition. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::mp_parent \brief The parent widget. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::m_referencePeakMz \brief The reference m/z value used for peak width calculations. This reference m/z value is used to compute the full width at half maximum of the shaped peak on the basis of the resolution and backwards. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::mp_config \brief The configuration of the pass peak shaping process. */ /*! \variable \ MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::m_normalizingIntensity \brief The intensity value that is used to normalize the peak height.. */ /*! \variable MsXpS::libXpertMassGui::MassPeakShaperConfigWidget::msp_msgTimer \brief The timer object used to clear messages to the messages line edit box. */ /*! \brief Constructs a MassPeakShaperConfigWidget instance. \list \li \a parent_p: the parent widget. \li \a config: the configuration of the mass peak shaping process \endlist */ MassPeakShaperConfigWidget::MassPeakShaperConfigWidget( const libXpertMassCore::MassPeakShaperConfig &config, QWidget *parent_p) : QWidget(parent_p), mp_ui(new Ui::MassPeakShaperConfigWidget), mp_config(new libXpertMassCore::MassPeakShaperConfig(config, this)) { // qDebug() << "Creating MassPeakShaperConfigWidget"; if(!parent_p) qFatal("Programming error. Program aborted."); setupWidget(); } /*! \brief Destructs this MassPeakShaperConfigWidget instance. */ MassPeakShaperConfigWidget::~MassPeakShaperConfigWidget() { // The timer should not try to access a deleted widget! qDebug() << "Stopping the timer."; if(msp_msgTimer.get()) msp_msgTimer.reset(); delete mp_ui; } /*! \brief Writes the settings of this widget to \a config_settings_file_path for later restoration. */ void MassPeakShaperConfigWidget::writeSettings( const QString &config_settings_file_path) { QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassPeakShaperConfigWidget"); settings.setValue("pointCount", mp_ui->pointCountSpinBox->value()); settings.setValue("resolution", mp_ui->resolutionSpinBox->value()); settings.setValue("fwhm", mp_ui->fwhmDoubleSpinBox->value()); settings.setValue("binSize", mp_ui->binSizeDoubleSpinBox->value()); settings.setValue("binSizeGroupBoxState", mp_ui->binSizeGroupBox->isChecked()); settings.setValue("fmwhToBinSizeDivisor", mp_ui->fmwhToBinSizeDivisorSpinBox->value()); settings.endGroup(); } /*! \brief Reads the settings of this widget from \a config_settings_file_path. */ void MassPeakShaperConfigWidget::readSettings( const QString &config_settings_file_path) { // qDebug(); QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("MassPeakShaperConfigWidget"); mp_ui->pointCountSpinBox->setValue(settings.value("pointCount", 150).toInt()); mp_ui->resolutionSpinBox->setValue( settings.value("resolution", 45000).toInt()); mp_ui->fwhmDoubleSpinBox->setValue(settings.value("fwhm", 0).toDouble()); mp_ui->binSizeGroupBox->setChecked( settings.value("binSizeGroupBoxState", 0).toInt()); mp_ui->binSizeDoubleSpinBox->setValue( settings.value("binSize", 0).toDouble()); mp_ui->fmwhToBinSizeDivisorSpinBox->setValue( settings.value("fwhmToBinSizeDivisor", 6).toInt()); settings.endGroup(); } /*! \brief Sets the reference m/z value to \a mz. */ void MassPeakShaperConfigWidget::setReferencePeakMz(double mz) { m_referencePeakMz = mz; mp_config->setReferencePeakMz(mz); mp_ui->referenceMassLineEdit->setText( QString::number(m_referencePeakMz, 'f', 12)); } /*! \brief Returns the reference m/z value. */ double MassPeakShaperConfigWidget::getReferencePeakMz() { if(m_referencePeakMz <= 0) { bool ok = false; m_referencePeakMz = mp_ui->referenceMassLineEdit->text().toDouble(&ok); if(!ok) { qDebug() << "Failed to convert string to double."; return 0; } } return m_referencePeakMz; } libXpertMassCore::MassPeakShaperConfig * MassPeakShaperConfigWidget::getConfig() { return mp_config; } /*! \brief Sets up this widget. */ void MassPeakShaperConfigWidget::setupWidget() { mp_ui->setupUi(this); // Ranges for various numerical values mp_ui->resolutionSpinBox->setRange(0, 2000000); mp_ui->fwhmDoubleSpinBox->setRange(0, 10); mp_ui->pointCountSpinBox->setRange(5, 1000); // By default we want a gaussian-type shape. mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN); mp_ui->gaussianRadioButton->setChecked(true); // Get the number of points used to craft the peak curve. mp_config->setPointCount(mp_ui->pointCountSpinBox->value()); // Set the message timer to be singleShot. This time is used to erase the text // shown in the message line edit widget after 3 seconds. msp_msgTimer->setInterval(3000); msp_msgTimer->setSingleShot(true); connect(msp_msgTimer.get(), &QTimer::timeout, [this]() { mp_ui->messageLineEdit->setText(""); }); connect(mp_ui->referenceMassLineEdit, &QLineEdit::textEdited, this, &MassPeakShaperConfigWidget::referencePeakMzEdited); connect(mp_ui->fmwhToBinSizeDivisorSpinBox, &QSpinBox::valueChanged, this, &MassPeakShaperConfigWidget::fwhmBinSizeDivisorValueChanged); connect(mp_ui->resolutionSpinBox, &QSpinBox::editingFinished, this, &MassPeakShaperConfigWidget::resolutionEditingFinished); connect(mp_ui->fwhmDoubleSpinBox, &QSpinBox::editingFinished, this, &MassPeakShaperConfigWidget::fwhmEditingFinished); connect(mp_ui->pointCountSpinBox, &QSpinBox::editingFinished, this, &MassPeakShaperConfigWidget::pointCountEditingFinished); connect(mp_ui->gaussianRadioButton, &QRadioButton::toggled, this, &MassPeakShaperConfigWidget::gaussianRadioButtonToggled); connect(mp_ui->lorentzianRadioButton, &QRadioButton::toggled, this, &MassPeakShaperConfigWidget::lorentzianRadioButtonToggled); connect(mp_ui->checkParametersPushButton, &QPushButton::clicked, this, qOverload<>(&MassPeakShaperConfigWidget::checkParameters)); connect(mp_ui->noBinsCheckBox, &QCheckBox::checkStateChanged, this, &MassPeakShaperConfigWidget::noBinsCheckBoxStateChanged); } /*! \brief Signals that the check box widget has changed \a state. */ void MassPeakShaperConfigWidget::noBinsCheckBoxStateChanged(int state) { if(state == Qt::Checked) { // The user does not want bins to be used. mp_ui->binSizeLogicGroupBox->setEnabled(false); mp_ui->binSizeDoubleSpinBox->setEnabled(false); } else { mp_ui->binSizeLogicGroupBox->setEnabled(true); mp_ui->binSizeDoubleSpinBox->setEnabled(true); } } /*! \brief Determines the bin configuration from the data entered by the user. Returns true if the calculations succeeded, false otherwise. */ bool MassPeakShaperConfigWidget::processBinSizeConfig() { // We want to understand what the user is expecting: create bins or not, // and do they want to have the bin size configured manually or do they // expect that the bin size be determined automatically. double bin_size = 0; bool set_bin_size_manually = mp_ui->binSizeGroupBox->isChecked(); bool no_bins = mp_ui->noBinsCheckBox->isChecked(); bool ok = false; // First off, report the usage of bins. If no bins are to be calculated, // then it is not necessary to bother with the fwhm / bin_size factor (see // below). if(no_bins) { // qDebug() << "The user requests no bins."; mp_config->setWithBins(false); mp_config->setBinSize(0); return true; } else { mp_config->setWithBins(true); // qDebug() << "The user requests bins, either automatic of manual."; if(set_bin_size_manually) { // We are checking the manual settings configuration. // qDebug() << "Configuring the bin size by direct setting."; mp_config->setBinSizeFixed(true); // Since the bin size is set manually, let the config object know that // it does not need to perform any computation. mp_config->setBinSizeDivisor(1); // Since the user checked the group box and they did not check the "no // bins" checkbox, then that means that they want to // actually set the bin size. bin_size = mp_ui->binSizeDoubleSpinBox->value(); if(!bin_size) { qDebug() << "The bin size cannot be 0."; message("The bin size cannot be 0."); return false; } mp_config->setBinSize(bin_size); // qDebug() << "Bin size set manually:" << bin_size; } else { // qDebug() << "Configuring the bin size by calculation."; // The user did not elect to set the bin size manually, we can // compute it and update the value in its spin box. // The mass peak shaper will compute the bin size simply by dividing // FWHM by that factor. The idea is that it takes roughly that value // data points to craft a peak that has that FWHM. int divisor = mp_ui->fmwhToBinSizeDivisorSpinBox->value(); if(!divisor) { qDebug() << "The FWHM / bin size divisor cannot be 0."; message( "The FWHM / bin size divisor cannot be 0. Please, fix it."); return false; } // That divisor will be accounted for in the config when // asked to compute the bin size = fwhm / divisor. An empirically good // value is 6. mp_config->setBinSizeDivisor(divisor); // qDebug() << "Set bin a size divisor of" << ratio; mp_config->setBinSizeFixed(false); double bin_size = mp_config->binSize(&ok); if(!ok) { message("Could not compute the bin size."); qDebug() << "Could not compute the bin size."; return false; } mp_ui->binSizeDoubleSpinBox->setValue(bin_size); // qDebug() << "The configuration has computed bin size:" << bin_size; } } return true; } /*! \brief Signals that the user has finished entering the resolving power value. */ void MassPeakShaperConfigWidget::resolutionEditingFinished() { double resolution = mp_ui->resolutionSpinBox->value(); if(!resolution) { // Tell the user to set a valid FWHM value, then. message("Will use the FWHM. Please, set a valid FWHM value"); return; } // At this point we know that resolution contains a proper value. mp_config->setResolution(resolution); // We can compute the FWHM using the resolution only if the reference peaks's // m/z value has been set. if(mp_config->getReferencePeakMz() <= 0) { message("Please fill-in the reference peak's m/z value."); return; } QString msg; msg += "Will try to use the resolution... "; message(msg); bool ok = false; double fwhm = mp_config->fwhm(&ok); if(!ok) { msg += "Failed. Check the parameters."; message(msg); return; } mp_ui->fwhmDoubleSpinBox->setValue(fwhm); // Check if we have enough of data to actually compute the bin size (or not // depending on the user's requirements). ok = processBinSizeConfig(); if(ok && mp_config->isWithBins()) { QString msg = QString("Could compute the bin size: %1") .arg(mp_config->getBinSize(), 0, 'f', 10); message(msg); // Since we used the resolution as the starting point, show that fact. mp_ui->resolutionBasedRadioButton->setChecked(true); } return; } /*! \brief Signals that the user has finished entering the FWHM value. The FWHM is the full width at half maximum, that is the width of the peak at its half-height. This value is a direct reflection of the resolving power of the mass spectrometer. */ void MassPeakShaperConfigWidget::fwhmEditingFinished() { double fwhm = mp_ui->fwhmDoubleSpinBox->value(); if(!fwhm) { // Tell the user to set a valid resolution value, then. message("Will use the resolution. Please, set a valid resolution value"); return; } // At this point we know that fwhm contains a proper value. mp_config->setFwhm(fwhm); // We can compute the resolution using FWHM only if the reference peaks's m/z // value has been set. if(mp_config->getReferencePeakMz() <= 0) { message("Please fill-in the reference peak's m/z value."); return; } QString msg; msg += "Will use the FWHM... "; message(msg); bool ok = false; double resolution = mp_config->resolution(&ok); // This is a peculiar situation, because from here we MUST be able to // compute the resolution, because all that is required is the FWHM, that we // have, and the reference peak centroid that SHOULD be there also. So, make // the error fatal. if(!ok) qFatal( "Programming error. At this point we should be able to compute the " "resolution."); mp_ui->resolutionSpinBox->setValue(resolution); // Check if we have enough of data to actually compute the bin size (or not // depending on the user's requirements). ok = processBinSizeConfig(); if(ok && mp_config->isWithBins()) { QString msg = QString("Could compute the bin size: %1") .arg(mp_config->getBinSize(), 0, 'f', 10); // Since we used the FWHM as the starting point, show that fact. mp_ui->fwhmBasedRadioButton->setChecked(true); message(msg); } return; } /*! \brief Signals that the user has finished editing the count of points needed to shape the peak. */ void MassPeakShaperConfigWidget::pointCountEditingFinished() { // The points have changed, we'll use that value to compute the m/z step // between two consecutive points in the shaped peak. // All we need is either FWHM or resolution to advance the configuration. // Just check what we have. int point_count = mp_ui->pointCountSpinBox->value(); if(point_count < 5) { message("The number of points to craft the shape is too small."); return; } mp_config->setPointCount(point_count); // At this point try to perform some calculations. double fwhm = mp_ui->fwhmDoubleSpinBox->value(); int resolution = mp_ui->resolutionSpinBox->value(); if(fwhm) fwhmEditingFinished(); else if(resolution) resolutionEditingFinished(); // The bin size configuration is handled by the functions above. // That's all we can do. } /*! \brief Signals that the user edited the reference m/z value with text \a text. */ void MassPeakShaperConfigWidget::referencePeakMzEdited(const QString &text) { bool ok = false; double mz = text.toDouble(&ok); if(!ok) { message("Please fix the reference peaks' m/z value."); return; } mp_config->setReferencePeakMz(mz); } /*! \brief Signals that the value by which the FWHM value should be divided is now \a value. The \a value is the number by which FWHM should be divided to yield the actual bin size. */ void MassPeakShaperConfigWidget::fwhmBinSizeDivisorValueChanged( [[maybe_unused]] int value) { // Recalculate all the bin size stuff. processBinSizeConfig(); } /*! \brief Signals that the peak shape type has changed. If \a checked is true, then the type is libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN, else it is libXpertMassCore::Enums::MassPeakShapeType::LORENTZIAN. */ void MassPeakShaperConfigWidget::gaussianRadioButtonToggled(bool checked) { if(checked) mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN); else mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::LORENTZIAN); } /*! \brief Signals that the peak shape type has changed. If \a checked is true, then the type is libXpertMassCore::Enums::MassPeakShapeType::LORENTZIAN, else it is libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN. */ void MassPeakShaperConfigWidget::lorentzianRadioButtonToggled(bool checked) { if(checked) mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::LORENTZIAN); else mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN); } /*! \brief Writes \a message to the message line edit widget. The message is erase after \a timeout in milliseconds. */ void MassPeakShaperConfigWidget::message(const QString &message, int timeout) { mp_ui->messageLineEdit->setText(message); msp_msgTimer->stop(); msp_msgTimer->setInterval(timeout); msp_msgTimer->start(); } /*! \brief Returns the resolving power spin box widget. */ QSpinBox * MassPeakShaperConfigWidget::getResolutionSpinBox() { return mp_ui->resolutionSpinBox; } /*! \brief Returns the FWHM spin box widget. */ QDoubleSpinBox * MassPeakShaperConfigWidget::getFwhmDoubleSpinBox() { return mp_ui->fwhmDoubleSpinBox; } /*! \brief Returns the FWHM to bin size divisor spin box widget. */ QSpinBox * MassPeakShaperConfigWidget::getFmwhToBinSizeDivisorSpinBox() { return mp_ui->fmwhToBinSizeDivisorSpinBox; } /*! \brief Returns the shape point count spin box widget. */ QSpinBox * MassPeakShaperConfigWidget::getPointCountSpinBox() { return mp_ui->pointCountSpinBox; } /*! \brief Returns the Gaussian shape type radio button widget. */ QRadioButton * MassPeakShaperConfigWidget::getGaussianRadioButton() { return mp_ui->gaussianRadioButton; } /*! \brief Returns the Lorentzian shape type radio button widget. */ QRadioButton * MassPeakShaperConfigWidget::getLorentzianRadioButton() { return mp_ui->lorentzianRadioButton; } void MassPeakShaperConfigWidget::setParameters( double reference_peak_mz, int point_count, libXpertMassCore::Enums::MassPeakShapeType mass_peak_shape_type, double resolution, double fwhm, int bin_size_divisor, bool should_create_bins) { mp_ui->referenceMassLineEdit->setText(QString::number(reference_peak_mz)); mp_ui->pointCountSpinBox->setValue(point_count); if(mass_peak_shape_type == libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN) mp_ui->gaussianRadioButton->setChecked(true); else mp_ui->gaussianRadioButton->setChecked(false); if(resolution) { mp_ui->resolutionSpinBox->setValue(resolution); mp_ui->resolutionBasedRadioButton->setChecked(true); } else { if(fwhm) { mp_ui->fwhmDoubleSpinBox->setValue(fwhm); mp_ui->fwhmBasedRadioButton->setChecked(true); } else { // Default: 40000 resolution mp_ui->resolutionSpinBox->setValue(40000); mp_ui->resolutionBasedRadioButton->setChecked(true); message("Set resolution to default value 40000"); } } if(!bin_size_divisor) bin_size_divisor = 6; mp_ui->fmwhToBinSizeDivisorSpinBox->setValue(bin_size_divisor); mp_ui->noBinsCheckBox->setCheckState(should_create_bins ? Qt::Unchecked : Qt::Checked); } void MassPeakShaperConfigWidget::setBinSizeManually(double bin_size, bool should_create_bins) { mp_ui->noBinsCheckBox->setCheckState(should_create_bins ? Qt::Unchecked : Qt::Checked); mp_ui->binSizeDoubleSpinBox->setValue(bin_size); } /*! \brief Checks that all the parameters are consistent and correct. Returns true is the check succeeded, false otherwise. \sa checkParameters() */ bool MassPeakShaperConfigWidget::checkParameters() { libXpertMassCore::ErrorList error_list; return checkParameters(&error_list); } /*! \overload \brief Checks that all the parameters are consistent and correct. If errors occurs, they are added to \a error_list_p. Returns true is the check succeeded, false otherwise. \sa checkParameters() */ bool MassPeakShaperConfigWidget::checkParameters( libXpertMassCore::ErrorList *error_list_p) { int initial_error_count = 0; if(error_list_p) initial_error_count = error_list_p->size(); QString msg; mp_config->reset(); // The general idea is that a number of parameters are inter-dependent. We // need to check these parameters in a precise order because of these // inter-dependencies. bool ok = false; m_referencePeakMz = QString(mp_ui->referenceMassLineEdit->text()).toDouble(&ok); if(!ok || m_referencePeakMz <= 0) { msg = "Please fill in the reference peak m/z value."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; } // We need to set back the reference peak centroid because the m_config was // reset above. mp_config->setReferencePeakMz(m_referencePeakMz); // We will need this value for computations below. int point_count = mp_ui->pointCountSpinBox->value(); if(point_count < 3) { msg = "The number of points allowed to craft the shape is too small."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; } else mp_config->setPointCount(point_count); // qDebug() << "The set point count:" << mp_config->getPointCount(); // Get to know if we want gaussian or lorentzian shapes. if(mp_ui->gaussianRadioButton->isChecked()) mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::GAUSSIAN); else mp_config->setMassPeakShapeType( libXpertMassCore::Enums::MassPeakShapeType::LORENTZIAN); // Now check the resolution/fwhm data that are competing one another. double fwhm = mp_ui->fwhmDoubleSpinBox->value(); int resolution = mp_ui->resolutionSpinBox->value(); // Both values cannot be naught. if(!resolution && !fwhm) { msg = "Please, fix the Resolution / FWHM value (one or the other)."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; // By default we favor the resolving power-based calculation. mp_ui->resolutionBasedRadioButton->setChecked(true); } if(resolution) { // We will use the resolution mp_config->setResolution(resolution); // We have to compute the FWHM value because that is a fundamental // parameter for the peak shaping. We use the most intense peak // centroid's mz value for that. bool ok = false; // With all the data successfully tested above, we must be able to // compute the FWHM. The config will store the computed value. double fwhm = mp_config->fwhm(&ok); if(!ok) { msg = "Failed to compute FWHM starting from resolution."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; // By default we favor the resolving power-based calculation. mp_ui->resolutionBasedRadioButton->setChecked(true); } mp_ui->fwhmDoubleSpinBox->setValue(fwhm); if(mp_config->getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::RESOLUTION) mp_ui->resolutionBasedRadioButton->setChecked(true); else if(mp_config->getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::FWHM) mp_ui->fwhmBasedRadioButton->setChecked(true); } // End of // if(resolution) else { // We will use the FWHM mp_config->setFwhm(fwhm); // We all the data above, we should be able to compute the resolution // and set it to the config. bool ok = false; resolution = mp_config->resolution(&ok); if(!ok) { msg = "Failed to compute the resolution."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; // By default we favor the resolving power-based calculation. mp_ui->resolutionBasedRadioButton->setChecked(true); } mp_ui->resolutionSpinBox->setValue(resolution); if(mp_config->getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::RESOLUTION) mp_ui->resolutionBasedRadioButton->setChecked(true); else if(mp_config->getMassPeakWidthLogic() == libXpertMassCore::Enums::MassPeakWidthLogic::FWHM) mp_ui->fwhmBasedRadioButton->setChecked(true); } // End of // ! if(resolution), that is use FWHM //////////////////// THE BIN SIZE ///////////////////// // qDebug() << "Now processing the bin size configuration."; if(!processBinSizeConfig()) { msg = "Failed to compute the bin size."; message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; } // Craft a string describing the whole set of params as we have been // working on them in the configuration instance. QString mass_peak_shaper_config_text = mp_config->toString(); qDebug().noquote() << "Configuration:" << mass_peak_shaper_config_text; libXpertMassCore::ErrorList error_list; if(!mp_config->resolve(error_list)) { msg = QString("Failed to finally resolve the configuration with errors:\n%1") .arg(libXpertMassCore::Utils::joinErrorList(error_list, "\n")); message(msg); if(error_list_p != nullptr) error_list_p->push_back(msg); qDebug() << msg; } if(error_list_p != nullptr && error_list_p->size() > initial_error_count) { QMessageBox::warning( this, "Mass peak shaper configuration - errors:", libXpertMassCore::Utils::joinErrorList(*error_list_p, "\n"), QMessageBox::Ok); return false; } // qDebug() << "Emitting signal with the configuration."; emit updatedMassPeakShaperConfigSignal(*mp_config); message("The parameters were validated successfully."); return true; } const libXpertMassCore::MassPeakShaperConfig * MassPeakShaperConfigWidget::getConfig() const { return mp_config; } void MassPeakShaperConfigWidget::registerJsConstructor(QJSEngine *engine) { if(!engine) { qWarning() << "Cannot register MassPeakShaperConfigWidget class: engine is null"; return; } // Register the meta object as a constructor QJSValue jsMetaObject = engine->newQMetaObject(&MassPeakShaperConfigWidget::staticMetaObject); engine->globalObject().setProperty("MassPeakShaperConfigWidget", jsMetaObject); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/ScriptingHistoryListWidget.cpp000664 001750 001750 00000022472 15100504560 030156 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include #include #include #include #include ///////////////////////////// Local includes #include "MsXpS/libXpertMassGui/ScriptingHistoryListWidget.hpp" namespace MsXpS { namespace libXpertMassGui { ScriptingHistoryListWidget::ScriptingHistoryListWidget(QWidget *parent) : QListWidget(parent) { setContextMenuPolicy(Qt::ActionsContextMenu); QString shortcut; QKeySequence keySequence; shortcut = "Ctrl+O, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); QAction *action = new QAction("Overwrite item(s) to Script input", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::overwriteSelectedItemsText); insertAction(Q_NULLPTR, action); shortcut = "Ctrl+I, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Insert item(s) to Script input", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::insertSelectedItemsText); insertAction(Q_NULLPTR, action); action = new QAction("", this); action->setSeparator(true); shortcut = "Ctrl+C, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Copy selected item(s) to clipboard", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::copySelectedItemsTextToClipboard); insertAction(Q_NULLPTR, action); action = new QAction("", this); action->setSeparator(true); shortcut = "Ctrl+R, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Remove Selected", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::removeSelected); insertAction(Q_NULLPTR, action); action = new QAction("", this); action->setSeparator(true); shortcut = "Ctrl+R, Ctrl+D"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Remove Duplicates when contiguous", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::removeDuplicates); insertAction(Q_NULLPTR, action); action = new QAction("", this); action->setSeparator(true); shortcut = "Ctrl+S, Ctrl+A"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Show All", this); action->setShortcut(keySequence); connect( action, &QAction::triggered, this, &ScriptingHistoryListWidget::showAll); insertAction(Q_NULLPTR, action); shortcut = "Ctrl+T, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Toggle Selection", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::toggleSelection); insertAction(Q_NULLPTR, action); shortcut = "Ctrl+H, Ctrl+S"; keySequence = QKeySequence::fromString(shortcut, QKeySequence::PortableText); action = new QAction("Hide Selected", this); action->setShortcut(keySequence); connect(action, &QAction::triggered, this, &ScriptingHistoryListWidget::hideSelected); insertAction(Q_NULLPTR, action); } ScriptingHistoryListWidget::~ScriptingHistoryListWidget() { } void ScriptingHistoryListWidget::keyPressEvent(QKeyEvent *event) { QListWidget::keyPressEvent(event); event->ignore(); } void ScriptingHistoryListWidget::showAll() { for(int iter = 0; iter < count(); ++iter) setRowHidden(iter, false); } void ScriptingHistoryListWidget::toggleSelection() { for(int iter = 0; iter < count(); ++iter) { bool isSelected = item(iter)->isSelected(); item(iter)->setSelected(!isSelected); } } void ScriptingHistoryListWidget::hideSelected() { for(int iter = 0; iter < count(); ++iter) { if(item(iter)->isSelected()) setRowHidden(iter, true); } } void ScriptingHistoryListWidget::removeSelected() { QList list = selectedItems(); for(int iter = 0; iter < list.size(); ++iter) { // Cannot understand why simply delete list.at(iter) does not work. // delete(list.at()); QListWidgetItem *curItem = takeItem(row(list.at(iter))); delete curItem; } } void ScriptingHistoryListWidget::removeDuplicates() { qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"; if(count() < 2) return; QList removeIndices; QList removeItems; QString prevText = item(0)->text(); for(int iter = 1; iter < count(); ++iter) { QListWidgetItem *curItem = item(iter); QString curText = curItem->text(); if(curText == prevText) { removeIndices.append(iter); removeItems.append(curItem); } prevText = curText; } // Now remove all the duplicate items. for(int iter = 0; iter < removeItems.size(); ++iter) { // Cannot understand why simply delete list.at(iter) does not work. // delete(list.at()); QListWidgetItem *curItem = takeItem(row(removeItems.at(iter))); delete curItem; } } void ScriptingHistoryListWidget::overwriteSelectedItemsText() { emit setSelectedItemsTextToScriptInput(selectedItemsText(), true /* overwrite */); } void ScriptingHistoryListWidget::insertSelectedItemsText() { emit setSelectedItemsTextToScriptInput(selectedItemsText(), false /* overwrite */); } void ScriptingHistoryListWidget::deselectAll() { for(int iter = 0; iter < count(); ++iter) { if(item(iter)->isSelected()) item(iter)->setSelected(false); } } void ScriptingHistoryListWidget::copySelectedItemsTextToClipboard() { QString text = selectedItemsText(); if(!text.isEmpty()) { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(text, QClipboard::Clipboard); if(clipboard->supportsSelection()) clipboard->setText(text, QClipboard::Selection); } } QString ScriptingHistoryListWidget::selectedItemsText() { #if 0 // In fact, we want to get the list of selected items. But beware, the // list is ordered in the selection chronological order, which is not what // we want. QList selItemList = selectedItems(); QString scriptContents; for(int iter = 0; iter < selItemList.size(); ++iter) { scriptContents.append(selItemList.at(iter)->text() + "\n"); } return scriptContents; #endif QString scriptContents; int itemCount = count(); for(int iter = 0; iter < itemCount; ++iter) { QListWidgetItem *curItem = item(iter); if(curItem->isSelected()) { QString itemText = item(iter)->text(); // We do not want to append a newline if that is the // last item of the list if(iter == itemCount - 1) scriptContents.append(itemText); else scriptContents.append(itemText + "\n"); } } return scriptContents; } void ScriptingHistoryListWidget::selectItemIndices(int startIndex, int endIndex) { // First of all, deselect all: deselectAll(); // And now start making the selection work. int itemCount = count(); int localStart = startIndex; int localEnd = endIndex; if(startIndex > endIndex) { localStart = endIndex; localEnd = startIndex; } if(localStart < 0) localStart = 0; if(localEnd >= itemCount) localEnd = itemCount - 1; for(int iter = localStart; iter <= localEnd; ++iter) { QListWidgetItem *curItem = item(iter); curItem->setSelected(true); } } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/ScriptingObjectTreeWidgetItem.cpp000664 001750 001750 00000006706 15100504560 030530 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * msXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2018 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the msXpertSuite project. * * The msXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Qt includes #include #include #include #include #include /////////////////////// libXpertMassCore includes #include "MsXpS/libXpertMassCore/Utils.hpp" /////////////////////// libXpertMassGUI includes ///////////////////////////// Local includes #include "MsXpS/libXpertMassGui/ScriptingObjectTreeWidgetItem.hpp" namespace MsXpS { namespace libXpertMassGui { ScriptingObjectTreeWidgetItem::ScriptingObjectTreeWidgetItem( QTreeWidget *parent, int type) : QObject(parent), QTreeWidgetItem(parent, type) { } ScriptingObjectTreeWidgetItem::ScriptingObjectTreeWidgetItem( QTreeWidgetItem *parent, int type) : QTreeWidgetItem(parent, type) { } ScriptingObjectTreeWidgetItem::~ScriptingObjectTreeWidgetItem() { } void ScriptingObjectTreeWidgetItem::setQObject(QObject *object) { if(object == Q_NULLPTR) qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__); mp_qobject = object; } void ScriptingObjectTreeWidgetItem::setText(int column, const QString &text) { QTreeWidgetItem::setText(column, text); } void ScriptingObjectTreeWidgetItem::setComment(const QString &comment) { m_comment = comment; } void ScriptingObjectTreeWidgetItem::setHelp(const QString &help) { m_help = help; setToolTip(0, libXpertMassCore::Utils::stanzifyParagraphs(m_help, 60)); setToolTip(1, libXpertMassCore::Utils::stanzifyParagraphs(m_help, 60)); } void ScriptingObjectTreeWidgetItem::setColor(const QColor &color) { m_color = color; QBrush brush(color); setForeground(/*column*/ 0, brush); setForeground(/*column*/ 1, brush); } void ScriptingObjectTreeWidgetItem::setScriptEntityType(ScriptEntityType type) { m_entityType = type; } void ScriptingObjectTreeWidgetItem::setProperties(QObject *object, const QString &comment, const QString &help, const QColor &color, ScriptEntityType entityType) { if(object != Q_NULLPTR) mp_qobject = object; m_comment = comment; m_help = help; setColor(color); m_entityType = entityType; } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/ToleranceWidget.cpp000664 001750 001750 00000020460 15100504560 025705 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright (C) 2009--2020 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// StdLib includes #include #include #include // for std::numeric_limits /////////////////////// Qt includes #include #include #include /////////////////////// pappsomspp includes /////////////////////// Local includes #include "MsXpS/libXpertMassCore/globals.hpp" #include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp" #include "MsXpS/libXpertMassGui/ToleranceWidget.hpp" #include "ui_ToleranceWidget.h" namespace MsXpS { namespace libXpertMassGui { /*! \class MsXpS::libXpertMassGui::ToleranceWidget \inmodule libXpertMassGui \ingroup XpertMassGuiMassCalculations \inheaderfile ToleranceWidget.hpp \brief The ToleranceWidget class provides a widget for the configuration of the mass tolerance. This composite widget contains all the widgets and all the logic required to fully configure the mass peak tolerance. */ /*! \variable MsXpS::libXpertMassGui::ToleranceWidget::mp_ui \brief The graphical user interface definition. */ /*! \brief Constructs a ToleranceWidget instance. \list \li \a parent_p: the parent widget. \li \a config_settings_file_path: the QSettings configuration file path \li \a nominal: the nominal libXpertMassCore::Tolerance value \li \a type: the libXpertMassCore::Tolerance::Type type \endlist */ ToleranceWidget::ToleranceWidget(QWidget *parent_p, const QString &config_settings_file_path, double nominal, libXpertMassCore::Tolerance::Type type) : QWidget(parent_p), m_configSettingsFilePath(config_settings_file_path), mp_tolerance(new libXpertMassCore::Tolerance(this)), mp_ui(new Ui::ToleranceWidget) { qDebug() << "Creating ToleranceWidget"; if(!parent_p) qFatal("Programming error. Program aborted."); mp_ui->setupUi(this); mp_ui->nominalDoubleSpinBox->setRange(0, 2000000); setupWidget(nominal, type); } /*! \brief Constructs a ToleranceWidget instance. \list \li \a parent_p: the parent widget. \li \a config_settings_file_path: the path to the configuration settings file \endlist */ ToleranceWidget::ToleranceWidget(QWidget *parent_p, const QString &config_settings_file_path) : QWidget(parent_p), m_configSettingsFilePath(config_settings_file_path), mp_tolerance(new libXpertMassCore::Tolerance(this)), mp_ui(new Ui::ToleranceWidget) { qDebug() << "Creating ToleranceWidget"; if(!parent_p) qFatal("Programming error. Program aborted."); mp_ui->setupUi(this); mp_ui->nominalDoubleSpinBox->setRange(0, 2000000); setupWidget(); } /*! \brief Destructs this ToleranceWidget instance. */ ToleranceWidget::~ToleranceWidget() { writeSettings(m_configSettingsFilePath); delete mp_ui; } /*! \brief Writes the settings of this widget to \a config_settings_file_path for later restoration. */ void ToleranceWidget::writeSettings(const QString &config_settings_file_path) { QSettings settings(config_settings_file_path, QSettings::IniFormat); settings.beginGroup("ToleranceWidget"); settings.setValue("nominal", mp_tolerance->getNominal()); libXpertMassCore::Tolerance::Type type = mp_tolerance->getType(mp_ui->typeComboBox->currentText()); settings.setValue("type", static_cast(type)); settings.endGroup(); } /*! \brief Sets the Tolerance nominal value to \a nominal. */ void ToleranceWidget::setNominal(double nominal) { if(nominal != mp_tolerance->getNominal()) mp_tolerance->setNominal(nominal); emit nominalChangedSignal(); } /*! \brief Returns the Tolerance nominal value. */ double ToleranceWidget::getNominal() const { return mp_tolerance->getNominal(); } /*! \brief Sets the Tolerance type to \a type. */ void ToleranceWidget::setType(libXpertMassCore::Tolerance::Type type) { if(type != mp_tolerance->getType()) mp_tolerance->setType(type); emit typeChangedSignal(); } /*! \brief Returns the Tolerance type . */ libXpertMassCore::Tolerance::Type ToleranceWidget::getType() const { return mp_tolerance->getType(); } /*! \brief Initializes the libXpertMassCore::Tolerance member using \a nominal and \a type. */ void ToleranceWidget::initializeTolerance(double nominal, libXpertMassCore::Tolerance::Type type) { mp_tolerance->setNominal(nominal); mp_tolerance->setType(type); } /*! \brief Initializes the libXpertMassCore::Tolerance member using \a nominal and \a type. */ void ToleranceWidget::initializeTolerance(const libXpertMassCore::Tolerance &tolerance) { mp_tolerance->initialize(tolerance); } /*! \brief Initializes the libXpertMassCore::Tolerance member using values read from the QSettings settings. If the settings are not found, the libXpertMassCore::Tolerance member is left untouched and has thus default values set in the class declaration. Returns true if the settings were found, false otherwise. */ bool ToleranceWidget::initializeToleranceFromSettings() { bool settings_found = true; QSettings settings(m_configSettingsFilePath, QSettings::IniFormat); settings.beginGroup("ToleranceWidget"); double nominal; libXpertMassCore::Tolerance::Type type; if(!settings.contains("nominal")) settings_found = false; else nominal = settings.value("nominal").toDouble(); if(!settings.contains("type")) settings_found = false; else type = static_cast( settings.value("type").toInt()); if(!settings_found) return false; mp_tolerance->setNominal(nominal); mp_tolerance->setType(type); return true; } /*! \brief Sets this widget up for the common data. */ void ToleranceWidget::setupWidgetCommon() { mp_ui->nominalDoubleSpinBox->setValue(mp_tolerance->getNominal()); mp_ui->typeComboBox->setCurrentText(mp_tolerance->getTypeAsString()); connect(mp_ui->nominalDoubleSpinBox, &QDoubleSpinBox::valueChanged, this, &ToleranceWidget::nominalDoubleSpinBoxValueChanged); connect(mp_ui->typeComboBox, &QComboBox::currentTextChanged, this, &ToleranceWidget::typeComboBoxValueChanged); } /*! \brief Sets this widget up. First the libXpertMassCore::Tolerance member is initialized from QSettings settings, if possible. Then the values are set to the widgets. */ void ToleranceWidget::setupWidget() { initializeToleranceFromSettings(); setupWidgetCommon(); } /*! \brief Sets this widget up using \a nominal and \a type. The libXpertMassCore::Tolerance member is initialized using the arguments and the values are set to the widgets. */ void ToleranceWidget::setupWidget(double nominal, libXpertMassCore::Tolerance::Type type) { initializeTolerance(nominal, type); setupWidgetCommon(); } /*! \brief Signals that the nominal value is now \a value. */ void ToleranceWidget::nominalDoubleSpinBoxValueChanged(double value) { mp_tolerance->setNominal(value); emit nominalChangedSignal(); } /*! \brief Signals that the Tolerance type is now described as \a text. */ void ToleranceWidget::typeComboBoxValueChanged(const QString &text) { mp_tolerance->setType(text); emit typeChangedSignal(); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/XpertMassGuiJavaScript.cpp000664 001750 001750 00000010705 15100504560 027210 0ustar00rusconirusconi000000 000000 #include "MsXpS/libXpertMassGui/XpertMassGuiJavaScript.hpp" namespace MsXpS { namespace libXpertMassGui { // This function needs to be called this way: // MsXpS::libXpertMassGui::registerEnumsToQJSEngine(engine_p) // in the program or library linking to this library. void registerEnumsToQJSEngine(QJSEngine *engine) { Q_UNUSED(engine) #if 0 At present there are no global enums in libXpertMassGui. // qDebug() << "Now registering the Enums:: enums for libXpertMassGui"; using namespace MsXpS::libXpertMassGui::Enums; // Create a JS object for libXpertMass QJSValue libraryJsInterface = engine->newObject(); // Create a nested JS object for Enums QJSValue all_js_enums = engine->newObject(); // Get the meta-object for the namespace (generated by Q_NAMESPACE) const QMetaObject *metaObject = &staticMetaObject; // Loop through all enums and populate jsEnums for(int i = 0; i < metaObject->enumeratorCount(); ++i) { QMetaEnum iter_meta_enum = metaObject->enumerator(i); // qDebug() << "Now iterating in enum:" << iter_meta_enum.enumName(); QJSValue single_js_enum = engine->newObject(); for(int j = 0; j < iter_meta_enum.keyCount(); ++j) { // qDebug() << "Now iterating in new key:" << iter_meta_enum.key(j) // << "with value:" << iter_meta_enum.value(j); // key() would be "LEFT", with value() = 1 single_js_enum.setProperty(iter_meta_enum.key(j), iter_meta_enum.value(j)); } // name would be CapType all_js_enums.setProperty(iter_meta_enum.name(), single_js_enum); } // Attach Enums to libXpertMassGui libraryJsInterface.setProperty("Enums", all_js_enums); engine->globalObject().setProperty("libXpertMassGui", libraryJsInterface); #ifdef QT_DEBUG qDebug() << "Now checking for correct exposition of Enums::enum:: pairs."; QJSValue jsEnums = engine->globalObject().property("libXpertMassGui").property("Enums"); if(jsEnums.isUndefined()) { qDebug() << "Error: libXpertMassGui.Enums not found!"; return; } else { qDebug() << "libXpertMassGui.Enums JS object exists"; // Get all enum names (properties of the Enums object) QJSValue propNames = engine->evaluate("Object.getOwnPropertyNames(libXpertMassGui.Enums)"); if(propNames.isError()) { qDebug() << "JS Error:" << propNames.toString(); return; } // Iterate through each enum QStringList enumNames = propNames.toVariant().toStringList(); for(const QString &enumName : enumNames) { QJSValue jsEnum = jsEnums.property(enumName); if(!jsEnum.isObject()) { qDebug() << "Skipping non-object property:" << enumName; continue; } qDebug() << "\nChecking enum:" << enumName; // Get all keys of the iterated enum QJSValue enumKeys = engine->evaluate( QString("Object.getOwnPropertyNames(libXpertMassGui.Enums.%1)") .arg(enumName)); if(enumKeys.isError()) { qDebug() << " JS Error:" << enumKeys.toString(); continue; } // Print key-value pairs for the iterated enum QStringList keys = enumKeys.toVariant().toStringList(); for(const QString &key : keys) { QJSValue value = jsEnum.property(key); qDebug() << " " << key << "=" << value.toInt(); } } } #endif // #ifdef QT_DEBUG #endif // #if 0 } // This function needs to be called this way: // MsXpS::libXpertMassGui::registerGlobalsToQJSEngine(engine_p) // in the program or library that links to this library. void registerGlobalsToQJSEngine(QJSEngine *engine) { // Create a JS object for the library QJSValue libraryJsInterface = engine->newObject(); // Create a nested JS object for Globals QJSValue js_globals = engine->newObject(); #if 0 js_globals.setProperty("ATOM_DEC_PLACES", ATOM_DEC_PLACES); js_globals.setProperty("OLIGOMER_DEC_PLACES", OLIGOMER_DEC_PLACES); js_globals.setProperty("POLYMER_DEC_PLACES", POLYMER_DEC_PLACES); js_globals.setProperty("PKA_PH_PI_DEC_PLACES", PKA_PH_PI_DEC_PLACES); #endif libraryJsInterface.setProperty("Globals", js_globals); engine->globalObject().setProperty("libXpertMassGui", libraryJsInterface); } } // namespace libXpertMassGui } // namespace MsXpS libxpertmass-1.4.0/source/XpertMassGui/src/ui/000775 001750 001750 00000000000 15100507137 022537 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/source/XpertMassGui/src/ui/DecimalPlacesOptionsDlg.ui000664 001750 001750 00000007576 15100504560 027603 0ustar00rusconirusconi000000 000000 DecimalPlacesOptionsDlg 0 0 246 273 0 0 massXpert: Decimal Places :/images/icons/32x32/massxpert.png:/images/icons/32x32/massxpert.png Configure the number of decimal places used to display numerical values for classes of chemical entities Qt::AlignmentFlag::AlignCenter Atoms: 0 0 pKa-pH-pI: Oligomers: Polymers: Qt::Orientation::Horizontal 40 20 &Validate Qt::Orientation::Horizontal 40 20 libxpertmass-1.4.0/source/XpertMassGui/src/ui/ElementGroupBoxWidget.ui000664 001750 001750 00000010577 15100504560 027330 0ustar00rusconirusconi000000 000000 ElementGroupBoxWidget 0 0 607 395 Form 110 50 250 124 Element Type the symbol, like C for Carbon 0 0 Remove this whole element Remove this entire Element definition :/images/svg/remove-chemical-element.svg:/images/svg/remove-chemical-element.svg QFrame::Shape::StyledPanel QFrame::Shadow::Raised Isotope's mass 0 0 Remove this isotope Remove this Isotope from this Element definition :/images/svg/remove-isotope.svg:/images/svg/remove-isotope.svg Isotope's abundance 0 0 Add a new isotope Add a new Isotope to this Element definition :/images/svg/add-isotope.svg:/images/svg/add-isotope.svg libxpertmass-1.4.0/source/XpertMassGui/src/ui/IsotopicClusterGeneratorDlg.ui000664 001750 001750 00000044006 15100504560 030530 0ustar00rusconirusconi000000 000000 IsotopicClusterGeneratorDlg Qt::WindowModality::NonModal 0 0 921 949 Dialog :/images/icons/32x32/minexpert2.png:/images/icons/32x32/minexpert2.png Qt::Orientation::Horizontal Configuration QFrame::Shape::Box QFrame::Shadow::Sunken The formula needs to account for the ionization level (charge below) Qt::TextFormat::PlainText Qt::AlignmentFlag::AlignCenter Ionization &Ion charge: ionChargeSpinBox 1 1000 Gaussian apex intensity value (a.u.) true 1 Max. cumulative probability 4 0.000000000000000 1.000000000000000 0.100000000000000 0.990000000000000 Cluster's centroids sorting &No sort true By &m/z false By &intensity (probability) Sort order &Ascending true &Descending Mass spectrum synthesis Set the title of the mass spectrum Select the color to be used for the trace plotting Trace color false Qt::Orientation::Vertical 20 40 0 IsoSpec standard static data Formula true 0 0 Add current formula to memory + 0 0 Remove current formula from memory - Actions Save table data to file for further editing false Run false IsoSpec standard user data Formula true 0 0 Add current formula to memory + 0 0 Remove current formula from memory - Actions Load isotopic data from file Save table data to file for further editing Run Manual configuration Qt::ScrollBarPolicy::ScrollBarAlwaysOn Qt::ScrollBarPolicy::ScrollBarAlwaysOn true 0 0 84 16 Actions Add element Run Load configuration Save configuration IsoSpec results To isotopic cluster shaper IsotopicDataTableView QTableView
MsXpS/libXpertMassGui/IsotopicDataTableView.hpp
libxpertmass-1.4.0/source/XpertMassGui/src/ui/IsotopicClusterShaperDlg.ui000664 001750 001750 00000055126 15100504560 030031 0ustar00rusconirusconi000000 000000 IsotopicClusterShaperDlg 0 0 836 644 This windows allows one to compute, for each (m/z,i) pair in the corresponding edit wiget, to compute all the points needed to create a peak shape in the form of either a Gaussian curve or of a Lorentzian curve. Once all the peaks have been computed they are combined to create a full mass spectrum with as many peaks as there are values in the Data points (m/z,i) text edit widget. 0 Input data Qt::Orientation::Horizontal 0 0 Data centroid points (format: m/z<space>i) Once the list of isotopic peaks has been edited in place or pasted, click the button above to validate the new text. To modify manually the peak centroid list edit the text below and click onto "Import from text" Import from text true 0 0 411 458 QFrame::Shape::StyledPanel QFrame::Shadow::Raised 0 0 Charge The charge reaction must have been accounted for Shape the peak centroids as charged ions: Note that the ionizing protons must have been accounted for in the m/z value. ionChargeSpinBox Charge level for which the isotopic cluster centroid peaks were calculated 1 10000 0 0 Normalize spectrum intensities true The most intense peak will have this intensity: 1 1000000000 0 0 Mass spectrum synthesis Set the title of the mass spectrum Select the color to be used for the trace plotting Trace color false 0 0 Actions &Run false false Choose a file in which to store the data for the plotting of the isotopic peak. &Output file... false &Check parameters Log Results 0 0 Mass spectrum name false true Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse Actions &Copy mass spectrum to clipboard &Display mass spectrum Help <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This mass peak shaper feature helps create the Gaussian peak shape around a peak centroid.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The Input data tab contains two different sides:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The left hand side contains the list of peak centroids for which the Gaussian/Lorentzian shape must be created;</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The right hand side contains all the parameters that are needed to configure the way the shape has to be created.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A Gaussian shape is characterized by three main parameters:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak position (the m/z value, in our case);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak height (intensity, in our case);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak width (full width at half maximum [FWHM], in our case).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak position (x axis) is the m/z value of the peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak height is the intensity of the peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak width (FWHM). The smallest this value, the thinnest the peak. The FWHM of a peak relates to the resolving power (R) of a mass spectrometer: the greater R is, the smaller FWHM will be (a highly resolutive instrument gives very thin peaks).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">In this window, the configuration logic is as follows:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user elicits to enter a resolving power value, then the program will compute automatically the FWHM on the basis of the m/z value of the most intense peak centroid. That value is updated in the corresponding spin box widget.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user elicits to enter a FWHM value, that value is going to be used irrespective of the resolving power value possibly displayed in the corresponding spin box widget.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The program then needs to determine the proper m/z bin size to properly combine all the peak shapes that have been computed into a single mass spectrum. The m/z bin size is determined according to the following logic:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user does not activate the corresponding group box, the program will update the bin size automatically based on the following parameters:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The m/z value of the most intense peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The FWHM (either set manually or deduced using the set resolving power);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The number of points requested for actually creating the peak shape (Points in the Peak shape group box).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Instead, if the user activates the &quot;Bin size&quot; group box, either two possibilities are presented:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user checks the &quot;Do not create bins&quot; check box, then the integration of all the peak shapes into a single mass spectrum will be performed without binning.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user sets a bin size manually, then, that bin size will be used no matter how they have configured the resolving power or the FWHM settings.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> outputFilePushButton runPushButton libxpertmass-1.4.0/source/XpertMassGui/src/ui/JavaScriptingWnd.ui000664 001750 001750 00000032370 15100504560 026315 0ustar00rusconirusconi000000 000000 JavaScriptingWnd 0 0 1069 847 MainWindow Qt::Orientation::Horizontal Feedback 11 Medium true Qt::Orientation::Horizontal Scripting The bottom edit widget is the input console, the top edit widget is the output console. Qt::Orientation::Vertical 5 &Abort script run :/images/red-cross-cancel.png:/images/red-cross-cancel.png QFrame::Shape::StyledPanel QFrame::Shadow::Raised Qt::Orientation::Vertical 0 History QFrame::Shape::StyledPanel QFrame::Shadow::Raised 0 0 QFrame::Shape::StyledPanel QFrame::Shadow::Raised Type expression to filter the text contents CaseSensitive true Configuration Qt::Orientation::Vertical General Qt::Orientation::Vertical Scripting History Logging Log on success Log on failure Save history Reset history Qt::Orientation::Vertical 20 102 JS reference -1 Type regular expression to filter the reference contents Verbatim CaseSensitive true Context lines: 0 0 0 0 1069 29 MsXpS::libXpertMassGui::ScriptingHistoryListWidget QWidget
MsXpS/libXpertMassGui/ScriptingHistoryListWidget.hpp
libxpertmass-1.4.0/source/XpertMassGui/src/ui/MassDataClientServerConfigDlg.ui000664 001750 001750 00000025550 15100504560 030702 0ustar00rusconirusconi000000 000000 MassDataClientServerConfigDlg Qt::WindowModality::NonModal 0 0 520 412 Mass data client/server configuration :/images/icons/32x32/minexpert2.png:/images/icons/32x32/minexpert2.png Client-Server configuration Server configuration (configure this running program as the local server) Qt::Orientation::Horizontal 40 20 Start server false Stop server false Qt::Orientation::Horizontal 40 20 Local IP address: localServerIpAddressLineEdit true false true Shows the IP address of the local server once started Local port number: localServerPortNumberLineEdit true false true Shows the port number of the local server once started Client configuration (configure this running program to connect to the remote server) Remote IP address: localServerIpAddressLineEdit 0 0 This IP address identifies the server that the client connects to true Enter the IP address of the server the client will connect to Clear the current IP address :/gui/images/svg/remove-chemical-element.svg:/gui/images/svg/remove-chemical-element.svg Remote port number: localServerPortNumberLineEdit 0 0 This port number identifies the port number that the client connects to true Enter the port number of the server the client will connect to Clear the current IP address :/gui/images/svg/remove-chemical-element.svg:/gui/images/svg/remove-chemical-element.svg Qt::Orientation::Horizontal 40 20 Start client false Stop client false Qt::Orientation::Horizontal 40 20 libxpertmass-1.4.0/source/XpertMassGui/src/ui/MassPeakShaperConfigDlg.ui000664 001750 001750 00000001642 15100504560 027522 0ustar00rusconirusconi000000 000000 MassPeakShaperConfigDlg 0 0 327 301 QFrame::Shape::StyledPanel QFrame::Shadow::Raised libxpertmass-1.4.0/source/XpertMassGui/src/ui/MassPeakShaperConfigWidget.ui000664 001750 001750 00000030755 15100504560 030246 0ustar00rusconirusconi000000 000000 MassPeakShaperConfigWidget 0 0 394 636 0 0 Form 0 0 Peak shaper configuration 0 0 Peak shape &Gaussian true &Lorentzian Shape the peak using points: Number of points that will make the peak shape 5 5000 200 0 0 Resolving power Full width at half maximum (the width of the peak at 50 % height) FWH&M: Qt::TextFormat::AutoText fwhmDoubleSpinBox The resolving power determines the FWHM The bin size will correspond to FWHM / ratio. Empirically, a good ratio is 6 if the number of points in the Gaussian/Lorentzian is 200. This somehow amounts to the idea that it takes 6 bins to make a peak of the FWHM. 1 10000 6 The bin size will correspond to FWHM / ratio. Empirically, a good ratio is 6 if the number of points in the Gaussian/Lorentzian is 200. This somehow amounts to the idea that it takes 6 bins to make a peak of the FWHM. FWHM to bin size divisor: Resolution of the mass spectrometer. 0 2000000 1000 0 Full width at half maximum (the width of the peak at 50 % height) 10 0.005000000000000 Resol&ving power: resolutionSpinBox Reference mass: This reference m/z value is used to relate the resolving power to the FWHM (full with at half maximum) peak width. Mass used to relate resolving power and FWHM 0 0 Bin size logic Qt::Orientation::Horizontal 40 20 Resolving power Qt::Orientation::Horizontal 40 20 FWHM Qt::Orientation::Horizontal 40 20 0 0 Manually set the bin size true false Do not create bins Bin si&ze (Th): binSizeDoubleSpinBox Set this parameter to a larger value if the peaks seem to be spiky 10 0.000000000000000 1.000000000000000 0.001000000000000 0.000000000000000 Check parameters libxpertmass-1.4.0/source/XpertMassGui/src/ui/MassPeakShaperDlg.ui000664 001750 001750 00000047412 15100504560 026401 0ustar00rusconirusconi000000 000000 MassPeakShaperDlg 0 0 629 755 This windows allows one to compute, for each (m/z,i) pair in the corresponding edit wiget, to compute all the points needed to create a peak shape in the form of either a Gaussian curve or of a Lorentzian curve. Once all the peaks have been computed they are combined to create a full mass spectrum with as many peaks as there are values in the Data points (m/z,i) text edit widget. 0 Input data Qt::Orientation::Horizontal 0 0 Data centroid points (format: m/z<space>i) To modify manually the peak centroid list edit the text below and click onto "Import from text" Import from text QFrame::Shape::StyledPanel QFrame::Shadow::Raised 0 0 Charge The charge reaction must have been accounted for Shape the peak centroids as charged ions: Note that the ionizing protons must have been accounted for in the m/z value. ionChargeSpinBox Charge level for which the isotopic cluster centroid peaks were calculated 1 10000 0 0 Normalize spectrum intensities true The most intense peak will have this intensity: 1 1000000000 0 0 Actions &Run true Choose a file in which to store the data for the plotting of the isotopic peak. &Output file... false &Check parameters Log Results 0 0 Mass spectrum name false true Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse Actions &Copy mass spectrum to clipboard &Display mass spectrum Help <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This mass peak shaper feature helps create the Gaussian peak shape around a peak centroid.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The Input data tab contains two different sides:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The left hand side contains the list of peak centroids for which the Gaussian/Lorentzian shape must be created;</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The right hand side contains all the parameters that are needed to configure the way the shape has to be created.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A Gaussian shape is characterized by three main parameters:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak position (the m/z value, in our case);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak height (intensity, in our case);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The peak width (full width at half maximum [FWHM], in our case).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak position (x axis) is the m/z value of the peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak height is the intensity of the peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The peak width (FWHM). The smallest this value, the thinnest the peak. The FWHM of a peak relates to the resolving power (R) of a mass spectrometer: the greater R is, the smaller FWHM will be (a highly resolutive instrument gives very thin peaks).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">In this window, the configuration logic is as follows:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user elicits to enter a resolving power value, then the program will compute automatically the FWHM on the basis of the m/z value of the most intense peak centroid. That value is updated in the corresponding spin box widget.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user elicits to enter a FWHM value, that value is going to be used irrespective of the resolving power value possibly displayed in the corresponding spin box widget.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The program then needs to determine the proper m/z bin size to properly combine all the peak shapes that have been computed into a single mass spectrum. The m/z bin size is determined according to the following logic:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user does not activate the corresponding group box, the program will update the bin size automatically based on the following parameters:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The m/z value of the most intense peak centroid;</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The FWHM (either set manually or deduced using the set resolving power);</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">- The number of points requested for actually creating the peak shape (Points in the Peak shape group box).</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Instead, if the user activates the &quot;Bin size&quot; group box, either two possibilities are presented:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user checks the &quot;Do not create bins&quot; check box, then the integration of all the peak shapes into a single mass spectrum will be performed without binning.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If the user sets a bin size manually, then, that bin size will be used no matter how they have configured the resolving power or the FWHM settings.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> outputFilePushButton runPushButton libxpertmass-1.4.0/source/XpertMassGui/src/ui/ToleranceWidget.ui000664 001750 001750 00000002722 15100504560 026156 0ustar00rusconirusconi000000 000000 ToleranceWidget 0 0 257 43 Form Configuration of the Tolerance The nominal tolerance value The nominal tolerance value QAbstractSpinBox::ButtonSymbols::NoButtons 5 2000000.000000000000000 The unit of the Tolerance The unit of the Tolerance libxpertmass-1.4.0/source/XpertMassGui/xpertmassgui_resources.qrc000664 001750 001750 00000000504 15100504560 026663 0ustar00rusconirusconi000000 000000 ../../images/svg/add-isotope.svg ../../images/svg/remove-isotope.svg ../../images/svg/remove-chemical-element.svg ../../doc/javascript/js_reference/Gui/class_js_reference_text.txt libxpertmass-1.4.0/tests/000775 001750 001750 00000000000 15100507137 016602 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/CMakeLists.txt000664 001750 001750 00000010532 15100504560 021340 0ustar00rusconirusconi000000 000000 message( "\n${BoldGreen}Now configuring tests for ${CMAKE_PROJECT_NAME}${ColourReset}") message("") message("To make the tests and then perform the coverage analysis:") message( "cmake ../../development -DBUILD_TESTS=On -DCODE_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug" ) message("make") message("make coverage") message( "And the coverage results will be output (HTML) in ${CMAKE_CURRENT_BINARY_DIR}/coverage" ) # This export will allow using the flags to be used by the Clang-based code # analyzer. set(CMAKE_EXPORT_COMPILE_COMMANDS 1) if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json") execute_process( COMMAND cmake -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json) endif() # This file is included if BUILD_TESTS option is set to ON configure_file(${CMAKE_UTILS_PATH}/tests-config.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/tests-config.h @ONLY) find_package(Catch2 3 REQUIRED) # Whatever the platform, this always works. find_package( Qt6 COMPONENTS Core Svg Xml Network REQUIRED) message("Using local XPERTMASS libraries build the tests") message("") set(XpertMass_FOUND 1) set(XpertMass_INCLUDE_DIRS "$ENV{HOME}/devel/xpertmass/development/src/XpertMass/includes") set(XpertMass_LIBRARIES "$ENV{HOME}/devel/xpertmass/build-area/unix/src/XpertMass/libXpertMass.so") if(NOT TARGET XpertMass::Core) add_library(XpertMass::Core UNKNOWN IMPORTED GLOBAL) set_target_properties( XpertMass::Core PROPERTIES IMPORTED_LOCATION ${XpertMass_LIBRARIES} INTERFACE_INCLUDE_DIRECTORIES ${XpertMass_INCLUDE_DIRS}) endif() message("") set(tests_SRCS catch2-test-main.cpp TestUtils.cpp test_CalcOptions.cpp test_Isotope.cpp test_IsotopicData.cpp test_IsotopicDataLibraryHandler.cpp test_IsotopicDataUserConfigHandler.cpp test_IsotopicDataManualConfigHandler.cpp test_Formula.cpp test_Ionizer.cpp test_ChemicalGroupRule.cpp test_ChemicalGroup.cpp test_IndexRangeCollection.cpp test_Modif.cpp test_Monomer.cpp test_Sequence.cpp test_CrossLinker.cpp test_CrossLinkedRegion.cpp test_CrossLink.cpp test_CleavageMotif.cpp test_CleavageRule.cpp test_CleavageAgent.cpp test_CleavageConfig.cpp test_FragmentationRule.cpp test_FragmentationPathway.cpp test_FragmentationConfig.cpp test_PolChemDef.cpp test_PolChemDefSpec.cpp test_Polymer.cpp test_Oligomer.cpp test_Cleaver.cpp test_Fragmenter.cpp test_MassCollection.cpp ) add_executable(testRunner ${tests_SRCS} ${${TARGET}_QRC_CPP}) # We set the target link libraries this way to reuse them later. set(TARGET_LINK_LIBRARIES -Wl,--whole-archive XpertMass::Core -Wl,--no-whole-archive -Wl,--no-as-needed PappsoMSpp::Core -Wl,--as-needed IsoSpec++::IsoSpec++ Qt6::Core Qt6::Xml Qt6::Network Catch2::Catch2WithMain) # Finally actually set the linking dependencies to the executable. target_link_libraries(testRunner ${TARGET_LINK_LIBRARIES}) # For the tests-config.h file target_include_directories(testRunner PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) set_property(TARGET testRunner PROPERTY CXX_STANDARD 17) # we want C++17 # Enable testing # enable_testing() include(Catch) catch_discover_tests(testRunner) # Add the Catch2-based single binary test file to the CMake's test suite so that # it gets called using 'make test'. To see the output, add "ARGS=-V" to the # call. add_test(NAME CoreTests COMMAND testRunner) # add_custom_target(runtests ALL add_custom_target(runtests COMMAND testRunner --success DEPENDS testRunner XpertMass::Core WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Now running the tests") if(CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE) include(CodeCoverage) append_coverage_compiler_flags() # Added to change the theme, by looking into the code set(GCOVR_ADDITIONAL_ARGS ${GCOVR_ADDITIONAL_ARGS} --html-theme github.dark-blue) # Added to ouput the gcovr command to the terminal, by looking into the code set(CODE_COVERAGE_VERBOSE On) setup_target_for_coverage_gcovr_html( NAME coverage EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/testRunner || /dev/true BASE_DIRECTORY ${CMAKE_SOURCE_DIR}) add_dependencies(coverage testRunner XpertMass::Core) endif() libxpertmass-1.4.0/tests/TestUtils.cpp000664 001750 001750 00000103273 15100504560 021251 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2024 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// libXpertMassCore includes #include #include #include /////////////////////// Local includes #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { TestUtils::TestUtils() { // Note the \" to enclose the title into \". m_actionFormulaStringMetAlaDipeptidylTitled = QString("\"%1\"%2") .arg(m_formulaTitle) .arg(m_actionFormulaStringMetAlaDipeptidyl); } TestUtils::TestUtils(const QString &pol_chem_def_name, int version) { initializePolChemDef(pol_chem_def_name, version); } TestUtils::~TestUtils() { } void TestUtils::initializeXpertmassLibrary() { libXpertMassCore::Utils xpertmass_utils; xpertmass_utils.configureDebugMessagesFormat(); } PolChemDefSPtr TestUtils::initializePolChemDef(const QString &pol_chem_def_name, int version) { initializeXpertmassLibrary(); if(pol_chem_def_name.isEmpty()) return nullptr; m_polChemDefName = pol_chem_def_name; m_polChemDefFileBaseName = QString("%1.xml").arg(m_polChemDefName); m_polChemDefDirName = m_polChemDefName; PolChemDef pol_chem_def; pol_chem_def.setName(m_polChemDefName); QString tests_pol_chem_def_input_versioned_dir = QString("%1/%2/version-%3") .arg(m_testsInputDataDir) .arg("polymer-chemistry-definitions") .arg(version); pol_chem_def.setXmlDataFilePath(QString("%1/%2/%3") .arg(tests_pol_chem_def_input_versioned_dir) .arg(m_polChemDefDirName) .arg(m_polChemDefFileBaseName)); msp_polChemDef = std::make_shared(pol_chem_def); m_isotopeCount = PolChemDef::loadIsotopicData(msp_polChemDef); qDebug() << "20241110"; if(!PolChemDef::renderXmlPolChemDefFile(msp_polChemDef)) { qFatalStream() << "Failed to render the PolChemDef."; } else { ErrorList error_list; msp_polChemDef->validate(&error_list); if(!msp_polChemDef->isValid()) qFatalStream() << "Failed to render the PolChemDef with errors:" << Utils::joinErrorList(error_list, ", "); } return msp_polChemDef; } QDomDocument TestUtils::craftFormulaDomDocument(const QStringList &dom_strings) { int formula_element_index = 0; int formula_text_index = 1; QDomDocument document; QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); document.appendChild(formula_element); return document; } QDomDocument TestUtils::craftIonizeruleDomDocument(const QStringList &dom_strings) { int ionizerule_element_index = 0; int formula_element_index = 1; int formula_text_index = 2; int charge_element_index = 3; int charge_text_index = 4; int level_element_index = 5; int level_text_index = 6; QDomDocument document; QDomElement ionizerule_element = document.createElement(dom_strings[ionizerule_element_index]); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); QDomElement charge_element = document.createElement(dom_strings[charge_element_index]); QDomText charge_text = document.createTextNode(dom_strings[charge_text_index]); charge_element.appendChild(charge_text); QDomElement level_element = document.createElement(dom_strings[level_element_index]); QDomText level_text = document.createTextNode(dom_strings[level_text_index]); level_element.appendChild(level_text); ionizerule_element.appendChild(formula_element); ionizerule_element.appendChild(charge_element); ionizerule_element.appendChild(level_element); document.appendChild(ionizerule_element); return document; } QDomDocument TestUtils::craftMdfDomDocument(const QStringList &dom_strings) { int mdf_element_index = 0; int name_element_index = 1; int name_text_index = 2; int formula_element_index = 3; int formula_text_index = 4; int targets_element_index = 5; int targets_text_index = 6; int maxcount_element_index = 7; int maxcount_text_index = 8; QDomDocument document; QDomElement mdf_element = document.createElement(dom_strings[mdf_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); QDomElement targets_element = document.createElement(dom_strings[targets_element_index]); QDomText targets_text = document.createTextNode(dom_strings[targets_text_index]); targets_element.appendChild(targets_text); QDomElement maxcount_element = document.createElement(dom_strings[maxcount_element_index]); QDomText maxcount_text = document.createTextNode(dom_strings[maxcount_text_index]); maxcount_element.appendChild(maxcount_text); mdf_element.appendChild(name_element); mdf_element.appendChild(formula_element); mdf_element.appendChild(targets_element); mdf_element.appendChild(maxcount_element); document.appendChild(mdf_element); return document; } QDomDocument TestUtils::craftMnmDomDocument(const QStringList &dom_strings) { int mnm_element_index = 0; int name_element_index = 1; int name_text_index = 2; int code_element_index = 3; int code_text_index = 4; int formula_element_index = 5; int formula_text_index = 6; QDomDocument document; QDomElement mnm_element = document.createElement(dom_strings[mnm_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); QDomElement code_element = document.createElement(dom_strings[code_element_index]); QDomText code_text = document.createTextNode(dom_strings[code_text_index]); code_element.appendChild(code_text); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); mnm_element.appendChild(name_element); mnm_element.appendChild(code_element); mnm_element.appendChild(formula_element); document.appendChild(mnm_element); return document; } QDomDocument TestUtils::craftMonomerDomDocument(const QStringList &dom_strings) { int monomer_element_index = 0; int code_element_index = 1; int code_text_index = 2; int mdf_element_index = 3; int name_element_index = 4; int name_text_index = 5; int formula_element_index = 6; int formula_text_index = 7; int targets_element_index = 8; int targets_text_index = 9; int maxcount_element_index = 10; int maxcount_text_index = 11; QDomDocument document; QDomElement monomer_element = document.createElement(dom_strings[monomer_element_index]); QDomElement code_element = document.createElement(dom_strings[code_element_index]); QDomText code_text = document.createTextNode(dom_strings[code_text_index]); code_element.appendChild(code_text); QDomElement mdf_element = document.createElement(dom_strings[mdf_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); // QString str; // QTextStream stream(&str); // QDomNode node = name_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The name element:" << str; QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); // QString str; // QTextStream stream(&str); // QDomNode node = formula_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The formula element:" << str; QDomElement targets_element = document.createElement(dom_strings[targets_element_index]); QDomText targets_text = document.createTextNode(dom_strings[targets_text_index]); targets_element.appendChild(targets_text); // QString str; // QTextStream stream(&str); // QDomNode node = targets_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The targets element:" << str; QDomElement maxcount_element = document.createElement(dom_strings[maxcount_element_index]); QDomText maxcount_text = document.createTextNode(dom_strings[maxcount_text_index]); maxcount_element.appendChild(maxcount_text); // QString str; // QTextStream stream(&str); // QDomNode node = maxcount_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The maxcount element:" << str; // // S // // Phosphorylation // H1O3P1 // * // 1 // // mdf_element.appendChild(name_element); mdf_element.appendChild(formula_element); mdf_element.appendChild(targets_element); mdf_element.appendChild(maxcount_element); // QString str; // QTextStream stream(&str); // QDomNode node = mdf_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The mdf element:" << str; monomer_element.appendChild(code_element); monomer_element.appendChild(mdf_element); // QString str; // QTextStream stream(&str); // QDomNode node = monomer_element; // node.save(stream, 4 /*indent*/); // qDebug() << "The monomer element:" << str; document.appendChild(monomer_element); return document; } QDomDocument TestUtils::craftClkDomDocument(const QStringList &dom_strings) { int clk_element_index = 0; int name_element_index = 1; int name_text_index = 2; int formula_element_index = 3; int formula_text_index = 4; int modif_name_element_index_0 = 5; int modif_name_text_index_0 = 6; int modif_name_element_index_1 = 7; int modif_name_text_index_1 = 8; int modif_name_element_index_2 = 9; int modif_name_text_index_2 = 10; QDomDocument document; QDomElement clk_element = document.createElement(dom_strings[clk_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); QDomElement modif_element_0 = document.createElement(dom_strings[modif_name_element_index_0]); QDomText modif_name_text_0 = document.createTextNode(dom_strings[modif_name_text_index_0]); modif_element_0.appendChild(modif_name_text_0); QDomElement modif_element_1 = document.createElement(dom_strings[modif_name_element_index_1]); QDomText modif_name_text_1 = document.createTextNode(dom_strings[modif_name_text_index_1]); modif_element_1.appendChild(modif_name_text_1); QDomElement modif_element_2 = document.createElement(dom_strings[modif_name_element_index_2]); QDomText modif_name_text_2 = document.createTextNode(dom_strings[modif_name_text_index_2]); modif_element_2.appendChild(modif_name_text_2); clk_element.appendChild(name_element); clk_element.appendChild(formula_element); clk_element.appendChild(modif_element_0); clk_element.appendChild(modif_element_1); clk_element.appendChild(modif_element_2); document.appendChild(clk_element); return document; } QDomDocument TestUtils::craftClrDomDocument(const QStringList &dom_strings) { // Totally fake clr !!! // // Homoseryl // A // +CH3COOH // M // -CH2S+O // int clr_element_index = 0; int name_element_index = 1; int name_text_index = 2; int le_mnm_code_element_index = 3; int le_mnm_code_text_index = 4; int le_formula_element_index = 5; int le_formula_text_index = 6; int re_mnm_code_element_index = 7; int re_mnm_code_text_index = 8; int re_formula_element_index = 9; int re_formula_text_index = 10; // QStringList dom_strings{"clr", // 0 // "name", // 1 // "Homoseryl", // 2 // "le-mnm-code", // 3 // "A", // 4 // "le-formula", // 5 // "+CH3COOH", // 6 // "re-mnm-code", // 7 // "M", // 8 // "re-formula", // 9 // "-CH2S+O" // 10 // }; QDomDocument document; QDomElement clr_element = document.createElement(dom_strings[clr_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); clr_element.appendChild(name_element); if(!dom_strings[le_mnm_code_element_index].isEmpty()) { QDomElement le_mnm_code_element = document.createElement(dom_strings[le_mnm_code_element_index]); QDomText le_mnm_code_text = document.createTextNode(dom_strings[le_mnm_code_text_index]); le_mnm_code_element.appendChild(le_mnm_code_text); clr_element.appendChild(le_mnm_code_element); QDomElement le_formula_element = document.createElement(dom_strings[le_formula_element_index]); QDomText le_formula_text = document.createTextNode(dom_strings[le_formula_text_index]); le_formula_element.appendChild(le_formula_text); clr_element.appendChild(le_formula_element); } if(!dom_strings[re_mnm_code_element_index].isEmpty()) { QDomElement re_mnm_code_element = document.createElement(dom_strings[re_mnm_code_element_index]); QDomText re_mnm_code_text = document.createTextNode(dom_strings[re_mnm_code_text_index]); re_mnm_code_element.appendChild(re_mnm_code_text); clr_element.appendChild(re_mnm_code_element); QDomElement re_formula_element = document.createElement(dom_strings[re_formula_element_index]); QDomText re_formula_text = document.createTextNode(dom_strings[re_formula_text_index]); re_formula_element.appendChild(re_formula_text); clr_element.appendChild(re_formula_element); } document.appendChild(clr_element); return document; } QDomDocument TestUtils::craftClsDomDocument(const QStringList &dom_strings) { // FAKE cls! // // CyanogenBromide // M/A // // M // -C1H2S1+O1 // A // -CH3COOH // // // QStringList dom_strings{"cls", // 0 // "name", // 1 // "CyanogenBromide", // 2 // "pattern", // 3 // "M/A", // 4 // "clr", // 5 // "name", // 6 // "Homoseryl", // 7 // "le-mnm-code", // 8 // "M", // 9 // "le-formula", // 10 // "-C1H2S1+O1", // 11 // "re-mnm-code", // 12 // "A", // 13 // "re-formula", // 14 // "-CH3COOH"}; // 15 int cls_element_index = 0; int name_element_index = 1; int name_text_index = 2; int pattern_element_index = 3; int pattern_text_index = 4; int clr_element_index = 5; int clr_name_element_index = 6; int clr_name_text_index = 7; int le_mnm_code_element_index = 8; int le_mnm_code_text_index = 9; int le_formula_element_index = 10; int le_formula_text_index = 11; int re_mnm_code_element_index = 12; int re_mnm_code_text_index = 13; int re_formula_element_index = 14; int re_formula_text_index = 15; QDomDocument document; QDomElement cls_element = document.createElement(dom_strings[cls_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); cls_element.appendChild(name_element); QDomElement pattern_element = document.createElement(dom_strings[pattern_element_index]); QDomText pattern_text = document.createTextNode(dom_strings[pattern_text_index]); pattern_element.appendChild(pattern_text); cls_element.appendChild(pattern_element); QDomElement clr_element = document.createElement(dom_strings[clr_element_index]); QDomElement clr_name_element = document.createElement(dom_strings[clr_name_element_index]); QDomText clr_name_text = document.createTextNode(dom_strings[clr_name_text_index]); clr_name_element.appendChild(clr_name_text); clr_element.appendChild(clr_name_element); QDomElement le_mnm_code_element = document.createElement(dom_strings[le_mnm_code_element_index]); QDomText le_mnm_code_text = document.createTextNode(dom_strings[le_mnm_code_text_index]); le_mnm_code_element.appendChild(le_mnm_code_text); clr_element.appendChild(le_mnm_code_element); QDomElement le_formula_element = document.createElement(dom_strings[le_formula_element_index]); QDomText le_formula_text = document.createTextNode(dom_strings[le_formula_text_index]); le_formula_element.appendChild(le_formula_text); clr_element.appendChild(le_formula_element); QDomElement re_mnm_code_element = document.createElement(dom_strings[re_mnm_code_element_index]); QDomText re_mnm_code_text = document.createTextNode(dom_strings[re_mnm_code_text_index]); re_mnm_code_element.appendChild(re_mnm_code_text); clr_element.appendChild(re_mnm_code_element); QDomElement re_formula_element = document.createElement(dom_strings[re_formula_element_index]); QDomText re_formula_text = document.createTextNode(dom_strings[re_formula_text_index]); re_formula_element.appendChild(re_formula_text); clr_element.appendChild(re_formula_element); cls_element.appendChild(clr_element); document.appendChild(cls_element); return document; } QDomDocument TestUtils::craftClaDomDocument(const QStringList &dom_strings) { // FAKE cla! // // CyanogenBromide // M/A // // M // -C1H2S1+O1 // A // -CH3COOH // // // QStringList dom_strings{"cla", // 0 // "name", // 1 // "CyanogenBromide", // 2 // "pattern", // 3 // "M/A", // 4 // "clr", // 5 // "name", // 6 // "Homoseryl", // 7 // "le-mnm-code", // 8 // "M", // 9 // "le-formula", // 10 // "-C1H2S1+O1", // 11 // "re-mnm-code", // 12 // "A", // 13 // "re-formula", // 14 // "-CH3COOH"}; // 15 int cla_element_index = 0; int name_element_index = 1; int name_text_index = 2; int pattern_element_index = 3; int pattern_text_index = 4; int clr_element_index = 5; int clr_name_element_index = 6; int clr_name_text_index = 7; int le_mnm_code_element_index = 8; int le_mnm_code_text_index = 9; int le_formula_element_index = 10; int le_formula_text_index = 11; int re_mnm_code_element_index = 12; int re_mnm_code_text_index = 13; int re_formula_element_index = 14; int re_formula_text_index = 15; QDomDocument document; QDomElement cla_element = document.createElement(dom_strings[cla_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); cla_element.appendChild(name_element); QDomElement pattern_element = document.createElement(dom_strings[pattern_element_index]); QDomText pattern_text = document.createTextNode(dom_strings[pattern_text_index]); pattern_element.appendChild(pattern_text); cla_element.appendChild(pattern_element); QDomElement clr_element = document.createElement(dom_strings[clr_element_index]); QDomElement clr_name_element = document.createElement(dom_strings[clr_name_element_index]); QDomText clr_name_text = document.createTextNode(dom_strings[clr_name_text_index]); clr_name_element.appendChild(clr_name_text); clr_element.appendChild(clr_name_element); QDomElement le_mnm_code_element = document.createElement(dom_strings[le_mnm_code_element_index]); QDomText le_mnm_code_text = document.createTextNode(dom_strings[le_mnm_code_text_index]); le_mnm_code_element.appendChild(le_mnm_code_text); clr_element.appendChild(le_mnm_code_element); QDomElement le_formula_element = document.createElement(dom_strings[le_formula_element_index]); QDomText le_formula_text = document.createTextNode(dom_strings[le_formula_text_index]); le_formula_element.appendChild(le_formula_text); clr_element.appendChild(le_formula_element); QDomElement re_mnm_code_element = document.createElement(dom_strings[re_mnm_code_element_index]); QDomText re_mnm_code_text = document.createTextNode(dom_strings[re_mnm_code_text_index]); re_mnm_code_element.appendChild(re_mnm_code_text); clr_element.appendChild(re_mnm_code_element); QDomElement re_formula_element = document.createElement(dom_strings[re_formula_element_index]); QDomText re_formula_text = document.createTextNode(dom_strings[re_formula_text_index]); re_formula_element.appendChild(re_formula_text); clr_element.appendChild(re_formula_element); cla_element.appendChild(clr_element); document.appendChild(cla_element); return document; } QDomDocument TestUtils::craftFgrDomDocument(const QStringList &dom_strings) { int fgr_element_index = 0; int name_element_index = 1; int name_text_index = 2; int formula_element_index = 3; int formula_text_index = 4; int prev_code_element_index = 5; int prev_code_text_index = 6; int current_code_element_index = 7; int current_code_text_index = 8; int next_code_element_index = 9; int next_code_text_index = 10; int comment_element_index = 11; int comment_text_index = 12; // QStringList dom_strings{"fgr", // 0 // "name", // "a-fgr-2", // "formula", // "+H100", // "prev-mnm-code", // "F", // "curr-mnm-code", // "D", // "next-mnm-code", // "E", // "comment", // "thecomment"}; QDomDocument document; QDomElement fgr_element = document.createElement(dom_strings[fgr_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); fgr_element.appendChild(name_element); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); fgr_element.appendChild(formula_element); // Totally fake fgr !!! // // a-fgr-2 // +H100 // F // D // E // thecomment // QDomElement prev_code_element = document.createElement(dom_strings[prev_code_element_index]); QDomText prev_code_text = document.createTextNode(dom_strings[prev_code_text_index]); prev_code_element.appendChild(prev_code_text); fgr_element.appendChild(prev_code_element); QDomElement current_code_element = document.createElement(dom_strings[current_code_element_index]); QDomText current_code_text = document.createTextNode(dom_strings[current_code_text_index]); current_code_element.appendChild(current_code_text); fgr_element.appendChild(current_code_element); QDomElement next_code_element = document.createElement(dom_strings[next_code_element_index]); QDomText next_code_text = document.createTextNode(dom_strings[next_code_text_index]); next_code_element.appendChild(next_code_text); fgr_element.appendChild(next_code_element); QDomElement comment_element = document.createElement(dom_strings[comment_element_index]); QDomText comment_text = document.createTextNode(dom_strings[comment_text_index]); comment_element.appendChild(comment_text); fgr_element.appendChild(comment_element); document.appendChild(fgr_element); return document; } QDomDocument TestUtils::craftFgsDomDocument(const QStringList &dom_strings) { qDebug() << "Size of the dom string list:" << dom_strings.size(); int fgs_element_index = 0; // int name_element_index = 1; int name_text_index = 2; int frag_end_element_index = 3; int frag_end_text_index = 4; int formula_element_index = 5; int formula_text_index = 6; int side_chain_element_index = 7; int side_chain_text_index = 8; int comment_element_index = 9; int comment_text_index = 10; // int fgr_element_index = 11; // int fgr_name_element_index = 12; int fgr_name_text_index = 13; int fgr_formula_element_index = 14; int fgr_formula_text_index = 15; int prev_code_element_index = 16; int prev_code_text_index = 17; int curr_code_element_index = 18; int curr_code_text_index = 19; int next_code_element_index = 20; int next_code_text_index = 21; int fgr_comment_element_index = 22; int fgr_comment_text_index = 23; // // a // LE // -C1O1 // 0 // opt_comment // // one_rule // +H2O // M // Y // T // opt_comment // // other fgr allowed, none possible also // QDomDocument document; QDomElement fgs_element = document.createElement(dom_strings[fgs_element_index]); QDomElement name_element = document.createElement(dom_strings[name_element_index]); QDomText name_text = document.createTextNode(dom_strings[name_text_index]); name_element.appendChild(name_text); fgs_element.appendChild(name_element); QDomElement frag_end_element = document.createElement(dom_strings[frag_end_element_index]); QDomText frag_end_text = document.createTextNode(dom_strings[frag_end_text_index]); frag_end_element.appendChild(frag_end_text); fgs_element.appendChild(frag_end_element); QDomElement formula_element = document.createElement(dom_strings[formula_element_index]); QDomText formula_text = document.createTextNode(dom_strings[formula_text_index]); formula_element.appendChild(formula_text); fgs_element.appendChild(formula_element); QDomElement side_chain_element = document.createElement(dom_strings[side_chain_element_index]); QDomText side_chain_text = document.createTextNode(dom_strings[side_chain_text_index]); side_chain_element.appendChild(side_chain_text); fgs_element.appendChild(side_chain_element); QDomElement comment_element = document.createElement(dom_strings[comment_element_index]); QDomText comment_text = document.createTextNode(dom_strings[comment_text_index]); comment_element.appendChild(comment_text); fgs_element.appendChild(comment_element); // // a // LE // -C1O1 // opt_comment // // one_rule // +H2O // M // Y // T // opt_comment // // other fgr allowed, none possible also // QDomElement fgr_element = document.createElement(dom_strings[fgr_element_index]); // int fgr_name_element_index = 10; // int fgr_name_text_index = 11; // int fgr_formula_element_index = 12; // int fgr_formula_text_index = 13; // int prev_code_element_index = 14; // int prev_code_text_index = 15; // int curr_code_element_index = 16; // int curr_code_text_index = 17; // int next_code_element_index = 18; // int next_code_text_index = 19; // int fgr_comment_element_index = 20; // int fgr_comment_text_index = 21; // // one_rule // +H2O // M // Y // T // opt_comment // QDomElement fgr_name_element = document.createElement(dom_strings[fgr_name_element_index]); QDomText fgr_name_text = document.createTextNode(dom_strings[fgr_name_text_index]); fgr_name_element.appendChild(fgr_name_text); fgr_element.appendChild(fgr_name_element); QDomElement fgr_formula_element = document.createElement(dom_strings[fgr_formula_element_index]); QDomText fgr_formula_text = document.createTextNode(dom_strings[fgr_formula_text_index]); fgr_formula_element.appendChild(fgr_formula_text); fgr_element.appendChild(fgr_formula_element); QDomElement prev_code_element = document.createElement(dom_strings[prev_code_element_index]); QDomText prev_code_text = document.createTextNode(dom_strings[prev_code_text_index]); prev_code_element.appendChild(prev_code_text); fgr_element.appendChild(prev_code_element); QDomElement curr_code_element = document.createElement(dom_strings[curr_code_element_index]); QDomText curr_code_text = document.createTextNode(dom_strings[curr_code_text_index]); curr_code_element.appendChild(curr_code_text); fgr_element.appendChild(curr_code_element); QDomElement next_code_element = document.createElement(dom_strings[next_code_element_index]); QDomText next_code_text = document.createTextNode(dom_strings[next_code_text_index]); next_code_element.appendChild(next_code_text); fgr_element.appendChild(next_code_element); QDomElement fgr_comment_element = document.createElement(dom_strings[fgr_comment_element_index]); QDomText fgr_comment_text = document.createTextNode(dom_strings[fgr_comment_text_index]); fgr_comment_element.appendChild(fgr_comment_text); fgr_element.appendChild(fgr_comment_element); fgs_element.appendChild(fgr_element); document.appendChild(fgs_element); return document; } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/TestUtils.hpp000664 001750 001750 00000014560 15100504560 021256 0ustar00rusconirusconi000000 000000 /* BEGIN software license * * MsXpertSuite - mass spectrometry software suite * ----------------------------------------------- * Copyright(C) 2009,...,2024 Filippo Rusconi * * http://www.msxpertsuite.org * * This file is part of the MsXpertSuite project. * * The MsXpertSuite project is the successor of the massXpert project. This * project now includes various independent modules: * * - massXpert, model polymer chemistries and simulate mass spectrometric data; * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner; * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * END software license */ /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include #include #include /////////////////////// libXpertMassCore includes #include #include /////////////////////// Local includes #include "tests-config.h" #pragma once namespace MsXpS { namespace libXpertMassCore { class DECLSPEC TestUtils { public: TestUtils(); TestUtils(const QString &pol_chem_def_name, int version); virtual ~TestUtils(); QString m_testsInputDataDir = QString(TESTS_INPUT_DIR); QString m_testsOutputDataDir = QString(TESTS_OUTPUT_DIR); QString m_testsInputPolChemDefsDir = QString("%1/polymer-chemistry-definitions").arg(m_testsInputDataDir); QString m_testsOutputPolChemDefsDir = QString("%1/polymer-chemistry-definitions").arg(m_testsOutputDataDir); QString m_testsInputPolSeqsDir = QString("%1/polymer-sequences").arg(m_testsInputDataDir); QString m_testsOutputPolSeqsDir = QString("%1/polymer-sequences").arg(m_testsOutputDataDir); QString m_polChemDefDirName; QString m_polChemDefFileBaseName; QString m_polChemDefName; PolChemDefSpec m_polChemDefSpec; PolChemDefSPtr msp_polChemDef = nullptr; QString xml_format_indent_string = " "; Isotope m_isotopeC12 = Isotope("carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); IsotopeQSPtr msp_isotopeC12 = std::make_shared(m_isotopeC12); Isotope m_isotopeC13 = Isotope("carbon", "C", 13.0033548352, 0.010788058149533083507343178553128382191061973571777343750000); IsotopeQSPtr msp_isotopeC13 = std::make_shared(m_isotopeC13); Isotope m_isotopeN14 = Isotope("nitrogen", "N", 14.0030740042, 0.996358014567941707717579902237048372626304626464843750000000); IsotopeQSPtr msp_isotopeN14 = std::make_shared(m_isotopeN14); Isotope m_isotopeN15 = Isotope("nitrogen", "N", 15.0001088994, 0.003641985432058271465738386041266494430601596832275390625000); IsotopeQSPtr msp_isotopeN15 = std::make_shared(m_isotopeN15); std::size_t m_isotopeCount = 0; QString m_acetylationFormulaString = "-H2O+CH3COOH"; QString m_tryptophanFormulaString = "C11H10N2O1"; // 157 residues QString m_telokinAsMonomerText1Letter = "MAMISGMSGRKASGSSPTSPINADKVENEDAFLEEVAEEKPHVKPYFTKTILDMEVVEGSAARFDCKIEGYPDP" "EVMWYKDDQPVKESRHFQIDYDEEGNCSLTISEVCGDDDAKYTCKAVNSLGEATCTAELLVETMGKEGEGEGEG" "EEDEEEEEE"; QString m_telokinNtermPeptideAsMonomerText1Letter = "MAMISGMSGRKASGSSPTSPINADKV"; int m_telokinSequenceMonomerCount = 157; // 157 residues, 7 spaces QString m_telokinAsMonomerText1LetterWithSpaces = "MAMISGMSGRKASGSSP TSPINADK " "VENEDAFLEEVAEEKPHVKPYFTKTILDMEVVEGSAARFDCKIEGYPDP" "EVMWYKDDQPVKESRHFQIDYDEEGNCSL TISEVCGDD DAKYTCKAVNSLG " "EATCTA\nELLVETMGKEGEGEGEG" "EEDE\tEEEEE"; // 157 residues QString m_telokinAsMonomerText3Letters = "MetAlaMetIleSerGlyMetSerGlyArgLysAlaSerGlySerSerProThrSerProIleAsnAlaAspLy" "sValGluAsnGluAspAlaPheLeuGluGluValAlaGluGluLysProHisValLysProTyrPheThrLysT" "hrIleLeuAspMetGluValValGluGlySerAlaAlaArgPheAspCysLysIleGluGlyTyrProAspPro" "GluValMetTrpTyrLysAspAspGlnProValLysGluSerArgHisPheGlnIleAspTyrAspGluGluGl" "yAsnCysSerLeuThrIleSerGluValCysGlyAspAspAspAlaLysTyrThrCysLysAlaValAsnSerL" "euGlyGluAlaThrCysThrAlaGluLeuLeuValGluThrMetGlyLysGluGlyGluGlyGluGlyGluGly" "GluGluAspGluGluGluGluGluGlu"; QString m_glucoseFormulaString = QString("C6H12O6"); QString m_saccharoseFormulaString = QString("C12H22O11"); QString m_formulaTitle = "MA dipeptide"; QString m_actionFormulaStringMetAlaDipeptidyl = QString("+C5H11N1O2S1-H2O+C3H7N1O2-H2O"); QString m_actionFormulaStringMetAlaDipeptidylSpaces = QString("+C5 H11 N1\nO2S1-H2O+C3 H7 N1 O2 - H2O"); // See the constructor QString m_actionFormulaStringMetAlaDipeptidylTitled; QString m_naturalIsotopicDataFileName = "light-c-n.dat"; QString m_manualUserIsotopicDataFileName = "one-C14-radioactive-on-glucose-manual-config.dat"; static void initializeXpertmassLibrary(); PolChemDefSPtr initializePolChemDef(const QString &pol_chem_def_name, int version); QDomDocument craftFormulaDomDocument(const QStringList &dom_strings); QDomDocument craftIonizeruleDomDocument(const QStringList &dom_strings); QDomDocument craftMdfDomDocument(const QStringList &dom_strings); QDomDocument craftMnmDomDocument(const QStringList &dom_strings); QDomDocument craftMonomerDomDocument(const QStringList &dom_strings); QDomDocument craftClkDomDocument(const QStringList &dom_strings); QDomDocument craftClrDomDocument(const QStringList &dom_strings); QDomDocument craftClsDomDocument(const QStringList &dom_strings); QDomDocument craftClaDomDocument(const QStringList &dom_strings); QDomDocument craftFgrDomDocument(const QStringList &dom_strings); QDomDocument craftFgsDomDocument(const QStringList &dom_strings); }; } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/catch2-test-main.cpp000664 001750 001750 00000000534 15100504560 022350 0ustar00rusconirusconi000000 000000 #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do // this in one cpp file #define CATCH_CONFIG_ENABLE_BENCHMARKING #ifdef CATCH2_MAJOR_VERSION_2 #include #elif CATCH2_MAJOR_VERSION_3 #include using namespace Catch; #endif // CATCH2_MAJOR_VERSION_2 libxpertmass-1.4.0/tests/data/000775 001750 001750 00000000000 15100507137 017513 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/isotopes/000775 001750 001750 00000000000 15100507137 021360 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/isotopes/light-c-n.dat000664 001750 001750 00000116230 15100504560 023634 0ustar00rusconirusconi000000 000000 # This file contains isotopic data in a format that can accommodate # comments in the form of lines beginning with the '#' character. hydrogen,H,1.007825032269999976364260874106548726558685302734375000000000,0.999884290164307909520857720053754746913909912109375000000000 hydrogen,H,2.014101778190000135992931973305530846118927001953125000000000,0.000115709835692033314582735648023970043141162022948265075684 helium,He,3.016029322000000068015879151062108576297760009765625000000000,0.000001342999991941999914655050951672876635711872950196266174 helium,He,4.002603254140000288430201180744916200637817382812500000000000,0.999998657000008006612290500925155356526374816894531250000000 lithium,Li,6.015122887100000426130463893059641122817993164062500000000000,0.075933925285977116326208147256693337112665176391601562500000 lithium,Li,7.016003442999999784035480843158438801765441894531250000000000,0.924066074714022800407065005856566131114959716796875000000000 beryllium,Be,9.012183159999999304545781342312693595886230468750000000000000,1.000000000000000000000000000000000000000000000000000000000000 boron,B,10.012937300000000817590262158773839473724365234375000000000000,0.199480830670926506664741850727295968681573867797851562500000 boron,B,11.009305299999999405713424494024366140365600585937500000000000,0.800519169329073410068531302385963499546051025390625000000000 carbon,C,12.000000000000000000000000000000000000000000000000000000000000,0.989211941850466902614869013632414862513542175292968750000000 carbon,C,13.003354835199999683936766814440488815307617187500000000000000,0.010788058149533083507343178553128382191061973571777343750000 nitrogen,N,14.003074004200000146624915942084044218063354492187500000000000,0.996358014567941707717579902237048372626304626464843750000000 nitrogen,N,15.000108899400000694868140271864831447601318359375000000000000,0.003641985432058271465738386041266494430601596832275390625000 oxygen,O,15.994914620199999433225457323715090751647949218750000000000000,0.997567609729561044495937949250219389796257019042968750000000 oxygen,O,16.999131757600000725005884305573999881744384765625000000000000,0.000380998476006095935803702490218825005285907536745071411133 oxygen,O,17.999159613700001614233769942075014114379882812500000000000000,0.002051391794432822109073288885383590240962803363800048828125 fluorine,F,18.998403163700000817470936453901231288909912109375000000000000,1.000000000000000000000000000000000000000000000000000000000000 neon,Ne,19.992440181999999282425051205791532993316650390625000000000000,0.904766666333356561757739200402284041047096252441406250000000 neon,Ne,20.993846730000001343796611763536930084228515625000000000000000,0.002709810313278070148523823945652111433446407318115234375000 neon,Ne,21.991385120000000341633494826965034008026123046875000000000000,0.092523523353365264010328417043638182803988456726074218750000 sodium,Na,22.989769282000001027199687086977064609527587890625000000000000,1.000000000000000000000000000000000000000000000000000000000000 magnesium,Mg,23.985041701000000102794729173183441162109375000000000000000000,0.789876809855211581279377242026384919881820678710937500000000 magnesium,Mg,24.985837029999999003848643042147159576416015625000000000000000,0.100001999840012789633192369365133345127105712890625000000000 magnesium,Mg,25.982593019999999484070940525270998477935791015625000000000000,0.110121190304775615209642580794024979695677757263183593750000 aluminium,Al,26.981538579999998717084963573142886161804199218750000000000000,1.000000000000000000000000000000000000000000000000000000000000 silicon,Si,27.976926535299998732853055116720497608184814453125000000000000,0.922220833349999713490774411184247583150863647460937500000000 silicon,Si,28.976494665299998843011053395457565784454345703125000000000000,0.046858437698747611166449900110819726251065731048583984375000 silicon,Si,29.973770011999999240970282698981463909149169921875000000000000,0.030920728951252581667707985957349592354148626327514648437500 phosphorus,P,30.973761998600000566739254281856119632720947265625000000000000,1.000000000000000000000000000000000000000000000000000000000000 sulfur,S,31.972071174100001655915548326447606086730957031250000000000000,0.949850011999040066967836537514813244342803955078125000000000 sulfur,S,32.971458910099997297038498800247907638549804687500000000000000,0.007519398448124149821059081233443066594190895557403564453125 sulfur,S,33.967867030000000738709786674007773399353027343750000000000000,0.042520598352131823427502155254842364229261875152587890625000 sulfur,S,35.967081200000002638716978253796696662902832031250000000000000,0.000109991200703943683199964587160479823069181293249130249023 chlorine,Cl,34.968852730000001827193045755848288536071777343750000000000000,0.757594848103037898923162174469325691461563110351562500000000 chlorine,Cl,36.965902640000003032128006452694535255432128906250000000000000,0.242405151896962045565686594272847287356853485107421875000000 argon,Ar,35.967545119999996927617758046835660934448242187500000000000000,0.003336205796380696270847510120916012965608388185501098632813 argon,Ar,37.962732199999997817485564155504107475280761718750000000000000,0.000629799206452999775149304007015871320618316531181335449219 argon,Ar,39.962383121999998536466591758653521537780761718750000000000000,0.996033994997166272078459314798237755894660949707031250000000 potassium,K,38.963706492999996555681718746200203895568847656250000000000000,0.932580526071084436878777523816097527742385864257812500000000 potassium,K,39.963998240000002226679498562589287757873535156250000000000000,0.000117099885242112454345267402722186034225160256028175354004 potassium,K,40.961825263000001484670065110549330711364746093750000000000000,0.067302374043673424131029037198459263890981674194335937500000 calcium,Ca,39.962590919999996685874066315591335296630859375000000000000000,0.969400838426726974006442105746828019618988037109375000000000 calcium,Ca,41.958618100000002471006155246868729591369628906250000000000000,0.006472228417153705684605746739634923869743943214416503906250 calcium,Ca,42.958766199999999457759258802980184555053710937500000000000000,0.001350985058105257227353823701321289263432845473289489746094 calcium,Ca,43.955482199999998726980265928432345390319824218750000000000000,0.020860869278785776348428271376178599894046783447265625000000 calcium,Ca,45.953691999999996653514244826510548591613769531250000000000000,0.000042999524425259849917842214228613784143817611038684844971 calcium,Ca,47.952522889999997346421878319233655929565429687500000000000000,0.001872079294802999303859447621789513505063951015472412109375 scandium,Sc,44.955908600000000774343789089471101760864257812500000000000000,1.000000000000000000000000000000000000000000000000000000000000 titanium,Ti,45.952628300000000649561116006225347518920898437500000000000000,0.082520097588289403889305617667559999972581863403320312500000 titanium,Ti,46.951759299999999086594471009448170661926269531250000000000000,0.074411070671519405350657905273692449554800987243652343750000 titanium,Ti,47.947942300000001125681592384353280067443847656250000000000000,0.737141543014838140912559083517407998442649841308593750000000 titanium,Ti,48.947866300000001160697138402611017227172851562500000000000000,0.054113506379234489751528514034362160600721836090087890625000 titanium,Ti,49.944787300000001550870365463197231292724609375000000000000000,0.051813782346118462951434224805780104361474514007568359375000 vanadium,V,49.947156700000000739692040951922535896301269531250000000000000,0.002503979968160254584302881752932989911641925573348999023438 vanadium,V,50.943957699999998567363945767283439636230468750000000000000000,0.997496020031839680797247638111002743244171142578125000000000 chromium,Cr,49.946042699999999570081854471936821937561035156250000000000000,0.043450743830478963380947732275672024115920066833496093750000 chromium,Cr,51.940506399999996745009411824867129325866699218750000000000000,0.837881075122238416774678171350387856364250183105468750000000 chromium,Cr,52.940648400000000606269168201833963394165039062500000000000000,0.095010483865806516501351097758742980659008026123046875000000 chromium,Cr,53.938879399999997588111000368371605873107910156250000000000000,0.023657697181476075587447382986283628270030021667480468750000 manganese,Mn,54.938044300000001385342329740524291992187500000000000000000000,1.000000000000000000000000000000000000000000000000000000000000 iron,Fe,53.939609300000000757790985517203807830810546875000000000000000,0.058452792721208068904559240763774141669273376464843750000000 iron,Fe,55.934936299999996833776094717904925346374511718750000000000000,0.917532497856775930422656983864726498723030090332031250000000 iron,Fe,56.935393300000001204352884087711572647094726562500000000000000,0.021190743592002535267138085828264593146741390228271484375000 iron,Fe,57.933274300000000778254616307094693183898925781250000000000000,0.002823965830013456732028309659199294401332736015319824218750 cobalt,Co,58.933194399999997870054357917979359626770019531250000000000000,1.000000000000000000000000000000000000000000000000000000000000 nickel,Ni,57.935342300000002069282345473766326904296875000000000000000000,0.680769095231327558970235713786678388714790344238281250000000 nickel,Ni,59.930786300000001176613295683637261390686035156250000000000000,0.262230419610671172669924544607056304812431335449218750000000 nickel,Ni,60.931056300000001613170752534642815589904785156250000000000000,0.011399083035777891892426083586542517878115177154541015625000 nickel,Ni,61.928345399999997766826709266752004623413085937500000000000000,0.036346250253448952882706635136855766177177429199218750000000 nickel,Ni,63.927967399999999997817212715744972229003906250000000000000000,0.009255151868774300419340228529563319170847535133361816406250 copper,Cu,62.929598400000003266541170887649059295654296875000000000000000,0.691494255172344751692037334578344598412513732910156250000000 copper,Cu,64.927790599999994469726516399532556533813476562500000000000000,0.308505744827655137285660202906001359224319458007812500000000 zinc,Zn,63.929142599999998708426574012264609336853027343750000000000000,0.491645713885820234700929631799226626753807067871093750000000 zinc,Zn,65.926034700000002430897438898682594299316406250000000000000000,0.277325508740183801492662496457342058420181274414062500000000 zinc,Zn,66.927128699999997252234607003629207611083984375000000000000000,0.040405292597461665848879164286699960939586162567138671875000 zinc,Zn,67.924845700000005876972863916307687759399414062500000000000000,0.184515103497573135227227680843498092144727706909179687500000 zinc,Zn,69.925321999999994204699760302901268005371093750000000000000000,0.006108381278961075126765489784474993939511477947235107421875 gallium,Ga,68.925574900000000866384652908891439437866210937500000000000000,0.601079797840404217446064194518839940428733825683593750000000 gallium,Ga,70.924703699999994910285749938338994979858398437500000000000000,0.398920202159595671531633342965506017208099365234375000000000 germanium,Ge,69.924249700000004281719157006591558456420898437500000000000000,0.205705812301332946478993335404084064066410064697265625000000 germanium,Ge,71.922075860000006741756806150078773498535156250000000000000000,0.274503726116209989527305879164487123489379882812500000000000 germanium,Ge,72.923459039999997344239091034978628158569335937500000000000000,0.077504017086240106770844704442424699664115905761718750000000 germanium,Ge,73.921177760999995598467648960649967193603515625000000000000000,0.364982406812098314485837136089685373008251190185546875000000 germanium,Ge,75.921402720000003228051355108618736267089843750000000000000000,0.077304037684118531714716482383664697408676147460937500000000 arsenic,As,74.921595699999997464146872516721487045288085937500000000000000,1.000000000000000000000000000000000000000000000000000000000000 selenium,Se,73.922475910000002841115929186344146728515625000000000000000000,0.008938426836876709608015190156038443092256784439086914062500 selenium,Se,75.919213720000001899279595818370580673217773437500000000000000,0.093712506598838590798905556766840163618326187133789062500000 selenium,Se,76.919914259999998762395989615470170974731445312500000000000000,0.076302570747548426055573145276866853237152099609375000000000 selenium,Se,77.917309200000005375841283239424228668212890625000000000000000,0.237686167234566703143627819372341036796569824218750000000000 selenium,Se,79.916522900000003915010893251746892929077148437500000000000000,0.496053694549759227605534306348999962210655212402343750000000 selenium,Se,81.916700099999999906685843598097562789916992187500000000000000,0.087306634032410290746639702774700708687305450439453125000000 bromine,Br,78.918338099999999712963472120463848114013671875000000000000000,0.506898896176611657438115798868238925933837890625000000000000 bromine,Br,80.916290099999997664781403727829456329345703125000000000000000,0.493101103823388231539581738616107031702995300292968750000000 krypton,Kr,77.920365599999996675251168198883533477783203125000000000000000,0.003552948126957346328819165037771199422422796487808227539063 krypton,Kr,79.916378600000001597436494193971157073974609375000000000000000,0.022860666234272977725971998097520554438233375549316406250000 krypton,Kr,81.913483700000000453655957244336605072021484375000000000000000,0.115931407401451927463575941601447993889451026916503906250000 krypton,Kr,82.914127199999995809776009991765022277832031250000000000000000,0.115000220996773441783922464765055337920784950256347656250000 krypton,Kr,83.911497733000004473069566302001476287841796875000000000000000,0.569863179997571966950431487930472940206527709960937500000000 krypton,Kr,85.910610633000004554560291580855846405029296875000000000000000,0.172791577242972227423933873069472610950469970703125000000000 rubidium,Rb,84.911789742999999930361809674650430679321289062500000000000000,0.721691132354705722207199869444593787193298339843750000000000 rubidium,Rb,86.909180535999993821860698517411947250366210937500000000000000,0.278308867645294166770497668039752170443534851074218750000000 strontium,Sr,83.913419899999993845085555221885442733764648437500000000000000,0.005609775608975640752429381308274969342164695262908935546875 strontium,Sr,85.909261900000004175126377958804368972778320312500000000000000,0.098606055757769678349333730693615507334470748901367187500000 strontium,Sr,86.908878900000004819048626814037561416625976562500000000000000,0.070007199712011511372189431767765199765563011169433593750000 strontium,Sr,87.905613900000005855872586835175752639770507812500000000000000,0.825776968921243081922511919401586055755615234375000000000000 yttrium,Y,88.905842000000006919435691088438034057617187500000000000000000,1.000000000000000000000000000000000000000000000000000000000000 zirconium,Zr,89.904702000000000339241523761302232742309570312500000000000000,0.514422711621750239352479638910153880715370178222656250000000 zirconium,Zr,90.905642000000000280124368146061897277832031250000000000000000,0.112234410554393593262290096390643157064914703369140625000000 zirconium,Zr,91.905032000000005609763320535421371459960937500000000000000000,0.171550886397901253266340404479706194251775741577148437500000 zirconium,Zr,93.906311999999999784449755679816007614135742187500000000000000,0.173788376250214926521664438041625544428825378417968750000000 zirconium,Zr,95.908271999999996637598087545484304428100585937500000000000000,0.028003615175739928616627238966430013533681631088256835937500 niobium,Nb,92.906372000000004618414095602929592132568359375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 molybdenum,Mo,91.906808600000005071706254966557025909423828125000000000000000,0.145308494342837241086741073559096548706293106079101562500000 molybdenum,Mo,93.905085299999996095721144229173660278320312500000000000000000,0.091496458524138415957516201615362660959362983703613281250000 molybdenum,Mo,94.905839299999996683254721574485301971435546875000000000000000,0.158387558641321063435114524509117472916841506958007812500000 molybdenum,Mo,95.904676300000005539914127439260482788085937500000000000000000,0.166690329831184980147185115129104815423488616943359375000000 molybdenum,Mo,96.906018299999999499050318263471126556396484375000000000000000,0.095999792030779435014764544575882609933614730834960937500000 molybdenum,Mo,97.905405299999998192106431815773248672485351562500000000000000,0.243900902666405350327494261364336125552654266357421875000000 molybdenum,Mo,99.907472799999993640085449442267417907714843750000000000000000,0.098216463963333416886669624545902479439973831176757812500000 ruthenium,Ru,95.907590299999995409052644390612840652465820312500000000000000,0.055402974808013198682044020415560225956141948699951171875000 ruthenium,Ru,97.905296000000006984009814914315938949584960937500000000000000,0.018726273471579152340993346115283202379941940307617187500000 ruthenium,Ru,98.905934799999997153463482391089200973510742187500000000000000,0.127588609866636532030881312493875157088041305541992187500000 ruthenium,Ru,99.904214800000005425317795015871524810791015625000000000000000,0.126054915071900669465421174209041055291891098022460937500000 ruthenium,Ru,100.905577899999997271152096800506114959716796875000000000000000,0.170586053375378299268305681835045106709003448486328125000000 ruthenium,Ru,101.904344899999998119710653554648160934448242187500000000000000,0.315451225206183960558803391904802992939949035644531250000000 ruthenium,Ru,103.905432000000004677531251218169927597045898437500000000000000,0.186189948200308125203505937861336860805749893188476562500000 rhodium,Rh,102.905501999999998474777385126799345016479492187500000000000000,1.000000000000000000000000000000000000000000000000000000000000 palladium,Pd,101.905602000000001794433046597987413406372070312500000000000000,0.010207550187954890497099569302008603699505329132080078125000 palladium,Pd,103.904031099999997422855813056230545043945312500000000000000000,0.111463248820283120088525663504697149619460105895996093750000 palladium,Pd,104.905080900000001520311343483626842498779296875000000000000000,0.223336399264176588275176982278935611248016357421875000000000 palladium,Pd,105.903480900000005249239620752632617950439453125000000000000000,0.273264416540030363744762098576757125556468963623046875000000 palladium,Pd,107.903892900000002441629476379603147506713867187500000000000000,0.264546508837878890929573572066146880388259887695312500000000 palladium,Pd,109.905172600000000215914042200893163681030273437500000000000000,0.117181876349676070137029171291942475363612174987792968750000 silver,Ag,106.905091999999996232872945256531238555908203125000000000000000,0.518389668985958174118877650471404194831848144531250000000000 silver,Ag,108.904755100000002698834578040987253189086914062500000000000000,0.481610331014041714858819887012941762804985046386718750000000 cadmium,Cd,105.906460899999999014653440099209547042846679687500000000000000,0.012567197514954164816458614950533956289291381835937500000000 cadmium,Cd,107.904183900000006701702659483999013900756835937500000000000000,0.008928009053980960965657409644791187020018696784973144531250 cadmium,Cd,109.903007400000007010021363385021686553955078125000000000000000,0.124890149496662231087817929164884844794869422912597656250000 cadmium,Cd,110.904183399999993753226590342819690704345703125000000000000000,0.127983459688489453753845737082883715629577636718750000000000 cadmium,Cd,111.902763399999997773193172179162502288818359375000000000000000,0.241267197414976458658131264201074372977018356323242187500000 cadmium,Cd,112.904408300000000053842086344957351684570312500000000000000000,0.122184752800125570604272695618419675156474113464355468750000 cadmium,Cd,113.903365300000004367575456853955984115600585937500000000000000,0.287277937020044504823346187549759633839130401611328125000000 cadmium,Cd,115.904763200000004985668056178838014602661132812500000000000000,0.074901297010766587636254598692175932228565216064453125000000 indium,In,112.904062699999997221311787143349647521972656250000000000000000,0.042954845418549769675564675708301365375518798828125000000000 indium,In,114.903878789000003735054633580148220062255859375000000000000000,0.957045154581450119302132861776044592261314392089843750000000 tin,Sn,111.904824399999995421239873394370079040527343750000000000000000,0.009707379007667929146641050408561568474397063255310058593750 tin,Sn,113.902783700000000521868059877306222915649414062500000000000000,0.006608215781738930282018795736576066701672971248626708984375 tin,Sn,114.903344709999998940475052222609519958496093750000000000000000,0.003409079548521898664348306340343697229400277137756347656250 tin,Sn,115.901743100000004460525815375149250030517578125000000000000000,0.145370749897527656857576516813423950225114822387695312500000 tin,Sn,116.902954300000004650428309105336666107177734375000000000000000,0.076859248003039171148742525474517606198787689208984375000000 tin,Sn,117.901607299999994893369148485362529754638671875000000000000000,0.242144620952342848330118840749491937458515167236328125000000 tin,Sn,118.903311599999994996323948726058006286621093750000000000000000,0.085916802463334898676272644024720648303627967834472656250000 tin,Sn,119.902202700000003687819116748869419097900390625000000000000000,0.325722055045137792728127124064485542476177215576171875000000 tin,Sn,121.903441999999998301973391789942979812622070312500000000000000,0.046317494276545329023875297025369945913553237915039062500000 tin,Sn,123.905277799999993249002727679908275604248046875000000000000000,0.057944355024143474885978122301821713335812091827392578125000 antimony,Sb,120.903812000000002058186510112136602401733398437500000000000000,0.572091349038115315472907695948379114270210266113281250000000 antimony,Sb,122.904212000000001125954440794885158538818359375000000000000000,0.427908650961884573504789841535966843366622924804687500000000 tellurium,Te,119.904061999999996146470948588103055953979492187500000000000000,0.000909764371027903685079651907585684966761618852615356445313 tellurium,Te,121.903041000000001758962753228843212127685546875000000000000000,0.025505394102927340937991829150632838718593120574951171875000 tellurium,Te,122.904270999999994273821357637643814086914062500000000000000000,0.008927687728878220055350745099076448241248726844787597656250 tellurium,Te,123.902821000000002982233127113431692123413085937500000000000000,0.047401722953754971134898710261040832847356796264648437500000 tellurium,Te,124.904431000000002427441359031945466995239257812500000000000000,0.070696689557404629455916733604681212455034255981445312500000 tellurium,Te,125.903311000000002195520210079848766326904296875000000000000000,0.188376210561464557668998054396070074290037155151367187500000 tellurium,Te,127.904461699999998813837009947746992111206054687500000000000000,0.317407791382032011817670991149498149752616882324218750000000 tellurium,Te,129.906222759000002042739652097225189208984375000000000000000000,0.340774739342510235573513455165084451436996459960937500000000 iodine,I,126.904472999999995863618096336722373962402343750000000000000000,1.000000000000000000000000000000000000000000000000000000000000 xenon,Xe,123.905891999999994368408806622028350830078125000000000000000000,0.000952296533640617525774685336870106766582466661930084228516 xenon,Xe,125.904302999999998746716300956904888153076171875000000000000000,0.000890196759683794711613680217254795934422872960567474365234 xenon,Xe,127.903531799999996110273059457540512084960937500000000000000000,0.019102830465697103606848017420816177036613225936889648437500 xenon,Xe,128.904780864000002793545718304812908172607421875000000000000000,0.264005869018636762923790683998959138989448547363281250000000 xenon,Xe,129.903509409999998069906723685562610626220703125000000000000000,0.040709981815666186621971434078659513033926486968994140625000 xenon,Xe,130.905084200000004557296051643788814544677734375000000000000000,0.212323527142361190289676642350968904793262481689453125000000 xenon,Xe,131.904155094000003600740456022322177886962890625000000000000000,0.269085350529324029977829013660084456205368041992187500000000 xenon,Xe,133.905395700000013903263607062399387359619140625000000000000000,0.104356830141138279266499466757522895932197570800781250000000 xenon,Xe,135.907214487999993934863596223294734954833984375000000000000000,0.088573117593851946605099101361702196300029754638671875000000 caesium,Cs,132.905451967000004742658347822725772857666015625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 barium,Ba,129.906321999999988747731549665331840515136718750000000000000000,0.001060985146207953045902061539607075246749445796012878417969 barium,Ba,131.905061799999998584098648279905319213867187500000000000000000,0.001010985846198153050023993415607037604786455631256103515625 barium,Ba,133.904508200000009310315363109111785888671875000000000000000000,0.024171461599537605313692267827718751505017280578613281250000 barium,Ba,134.905688199999985954491421580314636230468750000000000000000000,0.065920277116120362670415033790050074458122253417968750000000 barium,Ba,135.904576200000008157076081261038780212402343750000000000000000,0.078541300421794094099858796198532218113541603088378906250000 barium,Ba,136.905827200000004495450411923229694366455078125000000000000000,0.112320827508414877726750091824214905500411987304687500000000 barium,Ba,137.905247199999990925789461471140384674072265625000000000000000,0.716974162361726841119491382414707913994789123535156250000000 lanthanum,La,137.907123000000012780219549313187599182128906250000000000000000,0.000888171872103250392010975744483403104823082685470581054688 lanthanum,La,138.906362000000001444277586415410041809082031250000000000000000,0.999111828127896672846475212281802669167518615722656250000000 cerium,Ce,135.907129300000008242932381108403205871582031250000000000000000,0.001851973331584025024912354417949700291501358151435852050781 cerium,Ce,137.905998000000010961230145767331123352050781250000000000000000,0.002511963827720880421123794690174690913408994674682617187500 cerium,Ce,139.905441999999993640813045203685760498046875000000000000000000,0.884492463308528265031327464384958148002624511718750000000000 cerium,Ce,141.909252000000009275026968680322170257568359375000000000000000,0.111143599532166723053983048430382041260600090026855468750000 praseodymium,Pr,140.907661999999987756382324732840061187744140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 neodymium,Nd,141.907732000000009975337889045476913452148437500000000000000000,0.271519166958828106483991859931848011910915374755859375000000 neodymium,Nd,142.909821999999991248841979540884494781494140625000000000000000,0.121740433020292235233306143982190405949950218200683593750000 neodymium,Nd,143.910091999999991685399436391890048980712890625000000000000000,0.237977663997580829446931716120161581784486770629882812500000 neodymium,Nd,144.912581999999986237526172772049903869628906250000000000000000,0.082929723850915446070608538775559281930327415466308593750000 neodymium,Nd,145.913121999999987110641086474061012268066406250000000000000000,0.171890140355501652713599014532519504427909851074218750000000 neodymium,Nd,147.916901999999993222445482388138771057128906250000000000000000,0.057561075412857647115583148433870519511401653289794921875000 neodymium,Nd,149.920902000000012321834219619631767272949218750000000000000000,0.056381796404024006608146635244338540360331535339355468750000 samarium,Sm,143.912012000000004263711161911487579345703125000000000000000000,0.030772522277086666181444840617587033193558454513549804687500 samarium,Sm,146.914902000000012094460544176399707794189453125000000000000000,0.149881578776357327065227309503825381398200988769531250000000 samarium,Sm,147.914831999999989875504979863762855529785156250000000000000000,0.112382691006085513873991033051424892619252204895019531250000 samarium,Sm,148.917192000000000007275957614183425903320312500000000000000000,0.138246406123312015612469849656918086111545562744140625000000 samarium,Sm,149.917282000000000152795109897851943969726562500000000000000000,0.073792068527347848272412988990254234522581100463867187500000 samarium,Sm,151.919742000000013604221749119460582733154296875000000000000000,0.267451009404714612482933944193064235150814056396484375000000 samarium,Sm,153.922222000000004982211976312100887298583984375000000000000000,0.227473723885095902019770619517657905817031860351562500000000 europium,Eu,150.919861999999994850440998561680316925048828125000000000000000,0.478103065570820051632949798658955842256546020507812500000000 europium,Eu,152.921242000000006555637810379266738891601562500000000000000000,0.521896934429179837344747738825390115380287170410156250000000 gadolinium,Gd,151.919802000000004227331373840570449829101562500000000000000000,0.002009636255837693018938550082452820788603276014328002929688 gadolinium,Gd,153.920872000000002799424692057073116302490234375000000000000000,0.021826049485043207132317633067941642366349697113037109375000 gadolinium,Gd,154.922631999999993013261700980365276336669921875000000000000000,0.147985214676143617129611129712429828941822052001953125000000 gadolinium,Gd,155.922132000000004836692824028432369232177734375000000000000000,0.204672954195290635048820604424690827727317810058593750000000 gadolinium,Gd,156.923971999999992021912476047873497009277343750000000000000000,0.156491675006823760529783839956508018076419830322265625000000 gadolinium,Gd,157.924112000000008038114174269139766693115234375000000000000000,0.248435033258980114689862261911912355571985244750976562500000 gadolinium,Gd,159.927062000000006491973181255161762237548828125000000000000000,0.218579437121880937322515592313720844686031341552734375000000 terbium,Tb,158.925352000000003727109287865459918975830078125000000000000000,1.000000000000000000000000000000000000000000000000000000000000 dysprosium,Dy,155.924282000000005155015969648957252502441406250000000000000000,0.000562985756460361477619691594753703611786477267742156982422 dysprosium,Dy,157.924421999999992749508237466216087341308593750000000000000000,0.000952975889709990254573812595850768047966994345188140869141 dysprosium,Dy,159.925202000000012958480510860681533813476562500000000000000000,0.023291210732368467645203580218549177516251802444458007812500 dysprosium,Dy,160.926941999999996824044501408934593200683593750000000000000000,0.188889421097646226233024435714469291269779205322265625000000 dysprosium,Dy,161.926812000000012403688742779195308685302734375000000000000000,0.254747154896981076177553404704667627811431884765625000000000 dysprosium,Dy,162.928741999999999734427547082304954528808593750000000000000000,0.248957901365095435330943018925609067082405090332031250000000 dysprosium,Dy,163.929181999999997287886799313127994537353515625000000000000000,0.282598350261738351374418698469526134431362152099609375000000 holmium,Ho,164.930331999999992831362760625779628753662109375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 erbium,Er,161.928791999999987183400662615895271301269531250000000000000000,0.001395973476503946332158423437874716910300776362419128417969 erbium,Er,163.929212000000006810296326875686645507812500000000000000000000,0.016012695758780580435054474719436257146298885345458984375000 erbium,Er,165.930302000000011730662663467228412628173828125000000000000000,0.335027234482544788995994622382568195462226867675781250000000 erbium,Er,166.932051999999998770363163203001022338867187500000000000000000,0.228686654953555862368475004586798604577779769897460937500000 erbium,Er,167.932381999999989830030244775116443634033203125000000000000000,0.269776674243189351631855288360384292900562286376953125000000 erbium,Er,169.935472000000004300090949982404708862304687500000000000000000,0.149100767085425356395234075534972362220287322998046875000000 thulium,Tm,168.934222000000005436959327198565006256103515625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 ytterbium,Yb,167.933891999999985955582815222442150115966796875000000000000000,0.001232929969577727796758992440118163358420133590698242187500 ytterbium,Yb,169.934772000000009484210750088095664978027343750000000000000000,0.029822206098693591902470956256365752778947353363037109375000 ytterbium,Yb,170.936331999999993058736436069011688232421875000000000000000000,0.140905996539396560773838018576498143374919891357421875000000 ytterbium,Yb,171.936392000000012103555491194128990173339843750000000000000000,0.216800685721051017429417129278590437024831771850585937500000 ytterbium,Yb,172.938221999999996114638634026050567626953125000000000000000000,0.161027253651992552363481081556528806686401367187500000000000 ytterbium,Yb,173.938872000000003481545718386769294738769531250000000000000000,0.320249909805123023076589561242144554853439331054687500000000 ytterbium,Yb,175.942581999999987374394549988210201263427734375000000000000000,0.129961018214165419104588750087714288383722305297851562500000 lutetium,Lu,174.940782000000012885720934718847274780273437500000000000000000,0.974008767577204226384424146090168505907058715820312500000000 lutetium,Lu,175.942691999999993868186720646917819976806640625000000000000000,0.025991232422795697287742910930319339968264102935791015625000 hafnium,Hf,173.940052000000008547431207261979579925537109375000000000000000,0.001609652315099938373749166586890169128309935331344604492188 hafnium,Hf,175.941412000000013904355000704526901245117187500000000000000000,0.052668623577307296934613134453684324398636817932128906250000 hafnium,Hf,176.943231999999994741301634348928928375244140625000000000000000,0.185969830516608397585898160286888014525175094604492187500000 hafnium,Hf,177.943712000000004991306923329830169677734375000000000000000000,0.272821070648739838482299546740250661969184875488281250000000 hafnium,Hf,178.945821999999992613084032200276851654052734375000000000000000,0.136190582834107815068946933934057597070932388305664062500000 hafnium,Hf,179.946562000000000125510268844664096832275390625000000000000000,0.350740240108136591690168870627530850470066070556640625000000 tantalum,Ta,179.947462000000001580701791681349277496337890625000000000000000,0.000120131992311552486551486096377772128107608295977115631104 tantalum,Ta,180.948002000000002453816705383360385894775390625000000000000000,0.999879868007688354936135510797612369060516357421875000000000 tungsten,W,179.946711999999990894139045849442481994628906250000000000000000,0.001209872963338849303702171589236513682408258318901062011719 tungsten,W,181.948204699999990907599567435681819915771484375000000000000000,0.264988176241494621798722164385253563523292541503906250000000 tungsten,W,182.950223700000009330324246548116207122802734375000000000000000,0.143124971877952811283307710255030542612075805664062500000000 tungsten,W,183.950931700000012369855539873242378234863281250000000000000000,0.306387829277925793913794905165559612214565277099609375000000 tungsten,W,185.954362000000003263266989961266517639160156250000000000000000,0.284289149639287863635672692907974123954772949218750000000000 rhenium,Re,184.952955900000006295158527791500091552734375000000000000000000,0.374005039798408045470523575204424560070037841796875000000000 rhenium,Re,186.955750999999992245648172684013843536376953125000000000000000,0.625994960201591843507173962279921397566795349121093750000000 osmium,Os,183.952489100000008193092071451246738433837890625000000000000000,0.000209947723016968765524098428087995671376120299100875854492 osmium,Os,185.953841000000011263182386755943298339843750000000000000000000,0.015926034417430057904541129687459033448249101638793945312500 osmium,Os,186.955750999999992245648172684013843536376953125000000000000000,0.019615115836156795520173190539026109036058187484741210937500 osmium,Os,187.955840999999992391167324967682361602783203125000000000000000,0.132457018202467580181291850749403238296508789062500000000000 osmium,Os,188.958142000000009375071385875344276428222656250000000000000000,0.161519781574387955025429164379602298140525817871093750000000 osmium,Os,189.958441999999990912328939884901046752929687500000000000000000,0.262554623898649197588639481182326562702655792236328125000000 osmium,Os,191.961481999999989511707099154591560363769531250000000000000000,0.407717478347891348899878494194126687943935394287109375000000 iridium,Ir,190.960591999999991230652085505425930023193359375000000000000000,0.373050779688124722888176165724871680140495300292968750000000 iridium,Ir,192.962921999999991840013535693287849426269531250000000000000000,0.626949220311875166089521371759474277496337890625000000000000 platinum,Pt,189.959934000000004061803338117897510528564453125000000000000000,0.000121987349911814132899338936066868654961581341922283172607 platinum,Pt,191.961041999999991958247846923768520355224609375000000000000000,0.007821588901230941415221309398475568741559982299804687500000 platinum,Pt,193.962681699999990314609021879732608795166015625000000000000000,0.328605923565726210089366077227168716490268707275390625000000 platinum,Pt,194.964792700000003833338269032537937164306640625000000000000000,0.337788971283677852408544595164130441844463348388671875000000 platinum,Pt,195.964952699999997776103555224835872650146484375000000000000000,0.252107856415289710572125159160350449383258819580078125000000 platinum,Pt,197.967892000000006191839929670095443725585937500000000000000000,0.073553672484163390432598816914833150804042816162109375000000 gold,Au,196.966569600000013906537787988781929016113281250000000000000000,1.000000000000000000000000000000000000000000000000000000000000 mercury,Hg,195.965832000000006019035936333239078521728515625000000000000000,0.001509815802472098391837085351596670079743489623069763183594 mercury,Hg,197.966769300000009934592526406049728393554687500000000000000000,0.099707835644051417967048678292485419660806655883789062500000 mercury,Hg,198.968281300000001010630512610077857971191406250000000000000000,0.168701418426951910145561441822792403399944305419921875000000 mercury,Hg,199.968327299999998558632796630263328552246093750000000000000000,0.230990819120067331082779560347262304276227951049804687500000 mercury,Hg,200.970303599999994048630469478666782379150390625000000000000000,0.131793921141620695713925215386552736163139343261718750000000 mercury,Hg,201.970643599999988282434060238301753997802734375000000000000000,0.298589572072207154462830658303573727607727050781250000000000 mercury,Hg,203.973494299999998702332959510385990142822265625000000000000000,0.068706617792629293139938795320631470531225204467773437500000 thallium,Tl,202.972345100000012507734936662018299102783203125000000000000000,0.295204095918081610427918803907232359051704406738281250000000 thallium,Tl,204.974428100000011454540072008967399597167968750000000000000000,0.704795904081918278549778733577113598585128784179687500000000 lead,Pb,203.973044899999990775540936738252639770507812500000000000000000,0.014094362255097959285565778486670751590281724929809570312500 lead,Pb,205.974466900000010127769201062619686126708984375000000000000000,0.241003598560575765796798464180028531700372695922851562500000 lead,Pb,206.975897900000006757181836292147636413574218750000000000000000,0.221011595361855245345239495691203046590089797973632812500000 lead,Pb,207.976653900000002295200829394161701202392578125000000000000000,0.523890443822470963652904174523428082466125488281250000000000 bismuth,Bi,208.980401000000000522049958817660808563232421875000000000000000,1.000000000000000000000000000000000000000000000000000000000000 uranium,U,234.040952000000004318280844017863273620605468750000000000000000,0.000054599923560107009460132254652364736102754250168800354004 uranium,U,235.043932000000012294549378566443920135498046875000000000000000,0.007204689913434121108226637630878030904568731784820556640625 uranium,U,238.050792000000001280568540096282958984375000000000000000000000,0.992740710163005690702675565262325108051300048828125000000000 thorium,Th,232.038061999999996487531461752951145172119140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 protactinium,Pa,231.035881999999986646798788569867610931396484375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 libxpertmass-1.4.0/tests/data/isotopes/one-C14-radioactive-on-glucose-manual-config.dat000664 001750 001750 00000001747 15100504560 032245 0ustar00rusconirusconi000000 000000 # This file contains isotopic data in a format that can accomodate # comments in the form of lines beginning with the '#' character. [Element] symbol C count 5 [Isotopes] 2 mass 12.0 prob 0.98921194185046 mass 13.0033548352 prob 0.010788058149 [Element] symbol Cz count 1 [Isotopes] 3 # This position is 5% unoccupied by radioactive C14, thus, its # position has natural abundances for C12 and C13 compounded by 0.05. # C12: prob = 0.98921194185046 * 0.05 mass 12.0 prob 0.049460597092523 # C13: prob = 0.010788058149 * 0.05 mass 13.0033548352 prob 0.000539402907450 # This one is fixed at 0.95 because the vendor says incorporation of # C14 is 95%. mass 14.003241989 prob 0.95 [Element] symbol H count 12 [Isotopes] 2 mass 1.00782503227 prob 0.9998842901643 mass 2.01410177819 prob 0.00011570983 [Element] symbol O count 6 [Isotopes] 3 mass 15.9949146202 prob 0.99756760972956 mass 16.9991317576 prob 0.00038099847600 mass 17.9991596137 prob 0.0020513917944 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/000775 001750 001750 00000000000 15100507137 025520 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/000775 001750 001750 00000000000 15100507137 027343 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/000775 001750 001750 00000000000 15100507137 032456 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/acetyl.svg000664 001750 001750 00000006234 15100504560 034462 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/alanine.svg000664 001750 001750 00000003747 15100504560 034616 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/arginine.svg000664 001750 001750 00000005364 15100504560 035000 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/asparagine.svg000664 001750 001750 00000003672 15100504560 035237 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/aspartate.svg000664 001750 001750 00000004360 15100504560 035163 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/cfp-chromophore.svg000664 001750 001750 00000017430 15100504560 036215 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml T G F libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/chemPad.conf000664 001750 001750 00000014633 15100504560 034672 0ustar00rusconirusconi000000 000000 # This is the layout for the chempad of the molecular calculator 'extremecalc' # in the massXpert software program. # Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Filippo Rusconi. # GNU General Public License. # The format of this file allows the definition of colors # according to the following syntax: # # color%OneColor%xxx,yyy,zzz # # with xxx, yyy and zzz numerical values in range [0,255] for the red, # green and blue component of the color respectively. # # Available colors are: white, black, red, darkRed, green, darkGreen, # blue, darkBlue, cyan, darkCyan, magenta, darkMagenta, yellow, # darkYellow, gray, darkGray, lightGray, please, see # http://www.w3.org/TR/SVG/types.html#ColorKeywords for a list of # commonly used colors associated to their rgb value. # # A separator might be used between sections of buttons. Its syntax is: # Note the double && that acts as an escape of the &. # # chempadsep%Hexoses && Fucose%[midnightblue] # # The color specification (%[midnightblue]) is not compulsory # General button syntax is like the example below: # # chempadkey%hydrate%+H2O1%adds a water molecule%[red,blue] # # Each line is divided into 4 compulsory elements: # 'chempadkey' starts the line # 'hydrate' is the label of the button # '+H2O1' is the formula that is applied when the button gets clicked # 'adds a water molecule' is the text to display in the tooltip # '[red,blue]' are the background and foreground colors, respectively # # The colors specification (last %[color,color]) is not compulsory # # The geometry is specified by the number of columns of buttons: # chempad_columns%3 would specify that all the buttons in the # chemical pad should be laid out using three columns and as # many rows as necessary to display them all. chempad_columns%3 color%aliceblue%240,248,255 color%antiquewhite%250,235,215 color%aqua%0,255,255 color%aquamarine%127,255,212 color%azure%240,255,255 color%beige%245,245,220 color%bisque%255,228,196 color%black%0,0,0 color%blanchedalmond%255,235,205 color%blue%0,0,255 color%blueviolet%138,43,226 color%brown%165,42,42 color%burlywood%222,184,135 color%cadetblue%95,158,160 color%chartreuse%127,255,0 color%chocolate%210,105,30 color%coral%255,127,80 color%cornflowerblue%100,149,237 color%cornsilk%255,248,220 color%crimson%220,20,60 color%cyan%0,255,255 color%darkblue%0,0,139 color%darkcyan%0,139,139 color%darkgoldenrod%184,134,11 color%darkgray%169,169,169 color%darkgreen%0,100,0 color%darkgrey%169,169,169 color%darkkhaki%189,183,107 color%darkmagenta%139,0,139 color%darkolivegreen%85,107,47 color%darkorange%255,140,0 color%darkorchid%153,50,204 color%darkred%139,0,0 color%darksalmon%233,150,122 color%darkseagreen%143,188,143 color%darkslateblue%72,61,139 color%darkslategray%47,79,79 color%darkslategrey%47,79,79 color%darkturquoise%0,206,209 color%darkviolet%148,0,211 color%deeppink%255,20,147 color%deepskyblue%0,191,255 color%dimgray%105,105,105 color%dimgrey%105,105,105 color%dodgerblue%30,144,255 color%firebrick%178,34,34 color%floralwhite%255,250,240 color%forestgreen%34,139,34 color%fuchsia%255,0,255 color%gainsboro%220,220,220 color%ghostwhite%248,248,255 color%gold%255,215,0 color%goldenrod%218,165,32 color%gray%128,128,128 color%grey%128,128,128 color%green%0,128,0 color%greenyellow%173,255,47 color%honeydew%240,255,240 color%hotpink%255,105,180 color%indianred%205,92,92 color%indigo%75,0,130 color%ivory%255,255,240 color%khaki%240,230,140 color%lavender%230,230,250 color%lavenderblush%255,240,245 color%lawngreen%124,252,0 color%lemonchiffon%255,250,205 color%lightblue%173,216,230 color%lightcoral%240,128,128 color%lightcyan%224,255,255 color%lightgoldenrodyellow%250,250,210 color%lightgray%211,211,211 color%lightgreen%144,238,144 color%lightgrey%211,211,211 color%lightpink%255,182,193 color%lightsalmon%255,160,122 color%lightseagreen%32,178,170 color%lightskyblue%135,206,250 color%lightslategray%119,136,153 color%lightslategrey%119,136,153 color%lightsteelblue%176,196,222 color%lightyellow%255,255,224 color%lime%0,255,0 color%limegreen%50,205,50 color%linen%250,240,230 color%magenta%255,0,255 color%maroon%128,0,0 color%mediumaquamarine%102,205,170 color%mediumblue%0,0,205 color%mediumorchid%186,85,211 color%mediumpurple%147,112,219 color%mediumseagreen%60,179,113 color%mediumslateblue%123,104,238 color%mediumspringgreen%0,250,154 color%mediumturquoise%72,209,204 color%mediumvioletred%199,21,133 color%midnightblue%25,25,112 color%mintcream%245,255,250 color%mistyrose%255,228,225 color%moccasin%255,228,181 color%navajowhite%255,222,173 color%navy%0,0,128 color%oldlace%253,245,230 color%olive%128,128,0 color%olivedrab%107,142,35 color%orange%255,165,0 color%orangered%255,69,0 color%orchid%218,112,214 color%palegoldenrod%238,232,170 color%palegreen%152,251,152 color%paleturquoise%175,238,238 color%palevioletred%219,112,147 color%papayawhip%255,239,213 color%peachpuff%255,218,185 color%peru%205,133,63 color%pink%255,192,203 color%plum%221,160,221 color%powderblue%176,224,230 color%purple%128,0,128 color%red%255,0,0 color%rosybrown%188,143,143 color%royalblue%65,105,225 color%saddlebrown%139,69,19 color%salmon%250,128,114 color%sandybrown%244,164,96 color%seagreen%46,139,87 color%seashell%255,245,238 color%sienna%160,82,45 color%silver%192,192,192 color%skyblue%135,206,235 color%slateblue%106,90,205 color%slategray%112,128,144 color%slategrey%112,128,144 color%snow%255,250,250 color%springgreen%0,255,127 color%steelblue%70,130,180 color%tan%210,180,140 color%teal%0,128,128 color%thistle%216,191,216 color%tomato%255,99,71 color%turquoise%64,224,208 color%violet%238,130,238 color%wheat%245,222,179 color%white%255,255,255 color%whitesmoke%245,245,245 color%yellow%255,255,0 color%yellowgreen%154,205,50 chempad_columns%3 chempadgroup%Generic chempadkey%protonate%+H1%adds a proton chempadkey%hydrate%+H2O1%adds a water molecule chempadkey%0H-ylate%+O1H1%adds an hydroxyl group chempadkey%acetylate%-H1+C2H3O1%adds an acetyl group chempadkey%phosphorylate%-H+H2PO3%add a phosphate group chempadkey%sulfide bond%-H2%oxydizes with loss of hydrogen chempadgroup%Hexoses%[seagreen] chempadkey%Res-GlucNAc%+C8H14N1O6%residue GlcNAc%[lawngreen,black] chempadkey%Res-Lactoyl-MurNAc%+C11H17N1O6%residue MurNAc+Lactoyl%[lawngreen,black] chempadgroup%Step residues%[tomato] chempadkey%Res-mDAP%+C7H12N2O3%residue mDAP%[tomato,black] chempadkey%Res-iGlu%+C5H7N1O3%residue iGlu%[tomato,black] chempadkey%Res-Ala%+C3H5N1O1%residue Ala%[tomato,black] chempadkey%Res-iLys%+C6H12N2O2%residue iLys%[tomato,black] tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/cross_linker_dictionary000664 001750 001750 00000002204 15100504560 037237 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007,2008 Filippo Rusconi # This is the cross_linker_dictionary file where the correspondences # between the name of each cross-link and their graphic file (svg file # called "image") used to graphicallly render them in the sequence # editor are made. See the manual for details. # The format of the file is like this : # ------------------------------------- # DisulfideBond%disulfidebond.svg # where DisulfideBond is the name of the cross-link. disulfidebond.svg # is a resolution-independent svg file to be used to render the # cross-link vignette. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. DisulfideBond%disulfidebond-cross-link.svg CFP-chromophore%cfp-chromophore.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/cursor.svg000664 001750 001750 00000002144 15100504560 034512 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/cysteine.svg000664 001750 001750 00000004561 15100504560 035025 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/default-cross-link-vignette.svg000664 001750 001750 00000013740 15100504560 040453 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/default-modif-vignette.svg000664 001750 001750 00000017145 15100504560 037470 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/diaminopimelic.svg000664 001750 001750 00000006601 15100504560 036103 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml A P tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/disulfidebond-cross-link.svg000664 001750 001750 00000015615 15100504560 040022 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml S S libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/glutamate.svg000664 001750 001750 00000003751 15100504560 035165 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/glutamine.svg000664 001750 001750 00000005231 15100504560 035162 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/glutamylation.svg000664 001750 001750 00000003561 15100504560 036013 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml E libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/glycine.svg000664 001750 001750 00000004733 15100504560 034635 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/glycylation.svg000664 001750 001750 00000003605 15100504560 035457 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml G libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/histidine.svg000664 001750 001750 00000003751 15100504560 035162 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/hydroxylation.svg000664 001750 001750 00000005605 15100504560 036040 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/isoleucine.svg000664 001750 001750 00000003137 15100504560 035260 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/isotopic-data.dat000664 001750 001750 00000116230 15100504560 035631 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file contains isotopic data in a format that can accommodate # comments in the form of lines beginning with the '#' character. hydrogen,H,1.007825032269999976364260874106548726558685302734375000000000,0.999884290164307909520857720053754746913909912109375000000000 hydrogen,H,2.014101778190000135992931973305530846118927001953125000000000,0.000115709835692033314582735648023970043141162022948265075684 helium,He,3.016029322000000068015879151062108576297760009765625000000000,0.000001342999991941999914655050951672876635711872950196266174 helium,He,4.002603254140000288430201180744916200637817382812500000000000,0.999998657000008006612290500925155356526374816894531250000000 lithium,Li,6.015122887100000426130463893059641122817993164062500000000000,0.075933925285977116326208147256693337112665176391601562500000 lithium,Li,7.016003442999999784035480843158438801765441894531250000000000,0.924066074714022800407065005856566131114959716796875000000000 beryllium,Be,9.012183159999999304545781342312693595886230468750000000000000,1.000000000000000000000000000000000000000000000000000000000000 boron,B,10.012937300000000817590262158773839473724365234375000000000000,0.199480830670926506664741850727295968681573867797851562500000 boron,B,11.009305299999999405713424494024366140365600585937500000000000,0.800519169329073410068531302385963499546051025390625000000000 carbon,C,12.000000000000000000000000000000000000000000000000000000000000,0.989211941850466902614869013632414862513542175292968750000000 carbon,C,13.003354835199999683936766814440488815307617187500000000000000,0.010788058149533083507343178553128382191061973571777343750000 nitrogen,N,14.003074004200000146624915942084044218063354492187500000000000,0.996358014567941707717579902237048372626304626464843750000000 nitrogen,N,15.000108899400000694868140271864831447601318359375000000000000,0.003641985432058271465738386041266494430601596832275390625000 oxygen,O,15.994914620199999433225457323715090751647949218750000000000000,0.997567609729561044495937949250219389796257019042968750000000 oxygen,O,16.999131757600000725005884305573999881744384765625000000000000,0.000380998476006095935803702490218825005285907536745071411133 oxygen,O,17.999159613700001614233769942075014114379882812500000000000000,0.002051391794432822109073288885383590240962803363800048828125 fluorine,F,18.998403163700000817470936453901231288909912109375000000000000,1.000000000000000000000000000000000000000000000000000000000000 neon,Ne,19.992440181999999282425051205791532993316650390625000000000000,0.904766666333356561757739200402284041047096252441406250000000 neon,Ne,20.993846730000001343796611763536930084228515625000000000000000,0.002709810313278070148523823945652111433446407318115234375000 neon,Ne,21.991385120000000341633494826965034008026123046875000000000000,0.092523523353365264010328417043638182803988456726074218750000 sodium,Na,22.989769282000001027199687086977064609527587890625000000000000,1.000000000000000000000000000000000000000000000000000000000000 magnesium,Mg,23.985041701000000102794729173183441162109375000000000000000000,0.789876809855211581279377242026384919881820678710937500000000 magnesium,Mg,24.985837029999999003848643042147159576416015625000000000000000,0.100001999840012789633192369365133345127105712890625000000000 magnesium,Mg,25.982593019999999484070940525270998477935791015625000000000000,0.110121190304775615209642580794024979695677757263183593750000 aluminium,Al,26.981538579999998717084963573142886161804199218750000000000000,1.000000000000000000000000000000000000000000000000000000000000 silicon,Si,27.976926535299998732853055116720497608184814453125000000000000,0.922220833349999713490774411184247583150863647460937500000000 silicon,Si,28.976494665299998843011053395457565784454345703125000000000000,0.046858437698747611166449900110819726251065731048583984375000 silicon,Si,29.973770011999999240970282698981463909149169921875000000000000,0.030920728951252581667707985957349592354148626327514648437500 phosphorus,P,30.973761998600000566739254281856119632720947265625000000000000,1.000000000000000000000000000000000000000000000000000000000000 sulfur,S,31.972071174100001655915548326447606086730957031250000000000000,0.949850011999040066967836537514813244342803955078125000000000 sulfur,S,32.971458910099997297038498800247907638549804687500000000000000,0.007519398448124149821059081233443066594190895557403564453125 sulfur,S,33.967867030000000738709786674007773399353027343750000000000000,0.042520598352131823427502155254842364229261875152587890625000 sulfur,S,35.967081200000002638716978253796696662902832031250000000000000,0.000109991200703943683199964587160479823069181293249130249023 chlorine,Cl,34.968852730000001827193045755848288536071777343750000000000000,0.757594848103037898923162174469325691461563110351562500000000 chlorine,Cl,36.965902640000003032128006452694535255432128906250000000000000,0.242405151896962045565686594272847287356853485107421875000000 argon,Ar,35.967545119999996927617758046835660934448242187500000000000000,0.003336205796380696270847510120916012965608388185501098632813 argon,Ar,37.962732199999997817485564155504107475280761718750000000000000,0.000629799206452999775149304007015871320618316531181335449219 argon,Ar,39.962383121999998536466591758653521537780761718750000000000000,0.996033994997166272078459314798237755894660949707031250000000 potassium,K,38.963706492999996555681718746200203895568847656250000000000000,0.932580526071084436878777523816097527742385864257812500000000 potassium,K,39.963998240000002226679498562589287757873535156250000000000000,0.000117099885242112454345267402722186034225160256028175354004 potassium,K,40.961825263000001484670065110549330711364746093750000000000000,0.067302374043673424131029037198459263890981674194335937500000 calcium,Ca,39.962590919999996685874066315591335296630859375000000000000000,0.969400838426726974006442105746828019618988037109375000000000 calcium,Ca,41.958618100000002471006155246868729591369628906250000000000000,0.006472228417153705684605746739634923869743943214416503906250 calcium,Ca,42.958766199999999457759258802980184555053710937500000000000000,0.001350985058105257227353823701321289263432845473289489746094 calcium,Ca,43.955482199999998726980265928432345390319824218750000000000000,0.020860869278785776348428271376178599894046783447265625000000 calcium,Ca,45.953691999999996653514244826510548591613769531250000000000000,0.000042999524425259849917842214228613784143817611038684844971 calcium,Ca,47.952522889999997346421878319233655929565429687500000000000000,0.001872079294802999303859447621789513505063951015472412109375 scandium,Sc,44.955908600000000774343789089471101760864257812500000000000000,1.000000000000000000000000000000000000000000000000000000000000 titanium,Ti,45.952628300000000649561116006225347518920898437500000000000000,0.082520097588289403889305617667559999972581863403320312500000 titanium,Ti,46.951759299999999086594471009448170661926269531250000000000000,0.074411070671519405350657905273692449554800987243652343750000 titanium,Ti,47.947942300000001125681592384353280067443847656250000000000000,0.737141543014838140912559083517407998442649841308593750000000 titanium,Ti,48.947866300000001160697138402611017227172851562500000000000000,0.054113506379234489751528514034362160600721836090087890625000 titanium,Ti,49.944787300000001550870365463197231292724609375000000000000000,0.051813782346118462951434224805780104361474514007568359375000 vanadium,V,49.947156700000000739692040951922535896301269531250000000000000,0.002503979968160254584302881752932989911641925573348999023438 vanadium,V,50.943957699999998567363945767283439636230468750000000000000000,0.997496020031839680797247638111002743244171142578125000000000 chromium,Cr,49.946042699999999570081854471936821937561035156250000000000000,0.043450743830478963380947732275672024115920066833496093750000 chromium,Cr,51.940506399999996745009411824867129325866699218750000000000000,0.837881075122238416774678171350387856364250183105468750000000 chromium,Cr,52.940648400000000606269168201833963394165039062500000000000000,0.095010483865806516501351097758742980659008026123046875000000 chromium,Cr,53.938879399999997588111000368371605873107910156250000000000000,0.023657697181476075587447382986283628270030021667480468750000 manganese,Mn,54.938044300000001385342329740524291992187500000000000000000000,1.000000000000000000000000000000000000000000000000000000000000 iron,Fe,53.939609300000000757790985517203807830810546875000000000000000,0.058452792721208068904559240763774141669273376464843750000000 iron,Fe,55.934936299999996833776094717904925346374511718750000000000000,0.917532497856775930422656983864726498723030090332031250000000 iron,Fe,56.935393300000001204352884087711572647094726562500000000000000,0.021190743592002535267138085828264593146741390228271484375000 iron,Fe,57.933274300000000778254616307094693183898925781250000000000000,0.002823965830013456732028309659199294401332736015319824218750 cobalt,Co,58.933194399999997870054357917979359626770019531250000000000000,1.000000000000000000000000000000000000000000000000000000000000 nickel,Ni,57.935342300000002069282345473766326904296875000000000000000000,0.680769095231327558970235713786678388714790344238281250000000 nickel,Ni,59.930786300000001176613295683637261390686035156250000000000000,0.262230419610671172669924544607056304812431335449218750000000 nickel,Ni,60.931056300000001613170752534642815589904785156250000000000000,0.011399083035777891892426083586542517878115177154541015625000 nickel,Ni,61.928345399999997766826709266752004623413085937500000000000000,0.036346250253448952882706635136855766177177429199218750000000 nickel,Ni,63.927967399999999997817212715744972229003906250000000000000000,0.009255151868774300419340228529563319170847535133361816406250 copper,Cu,62.929598400000003266541170887649059295654296875000000000000000,0.691494255172344751692037334578344598412513732910156250000000 copper,Cu,64.927790599999994469726516399532556533813476562500000000000000,0.308505744827655137285660202906001359224319458007812500000000 zinc,Zn,63.929142599999998708426574012264609336853027343750000000000000,0.491645713885820234700929631799226626753807067871093750000000 zinc,Zn,65.926034700000002430897438898682594299316406250000000000000000,0.277325508740183801492662496457342058420181274414062500000000 zinc,Zn,66.927128699999997252234607003629207611083984375000000000000000,0.040405292597461665848879164286699960939586162567138671875000 zinc,Zn,67.924845700000005876972863916307687759399414062500000000000000,0.184515103497573135227227680843498092144727706909179687500000 zinc,Zn,69.925321999999994204699760302901268005371093750000000000000000,0.006108381278961075126765489784474993939511477947235107421875 gallium,Ga,68.925574900000000866384652908891439437866210937500000000000000,0.601079797840404217446064194518839940428733825683593750000000 gallium,Ga,70.924703699999994910285749938338994979858398437500000000000000,0.398920202159595671531633342965506017208099365234375000000000 germanium,Ge,69.924249700000004281719157006591558456420898437500000000000000,0.205705812301332946478993335404084064066410064697265625000000 germanium,Ge,71.922075860000006741756806150078773498535156250000000000000000,0.274503726116209989527305879164487123489379882812500000000000 germanium,Ge,72.923459039999997344239091034978628158569335937500000000000000,0.077504017086240106770844704442424699664115905761718750000000 germanium,Ge,73.921177760999995598467648960649967193603515625000000000000000,0.364982406812098314485837136089685373008251190185546875000000 germanium,Ge,75.921402720000003228051355108618736267089843750000000000000000,0.077304037684118531714716482383664697408676147460937500000000 arsenic,As,74.921595699999997464146872516721487045288085937500000000000000,1.000000000000000000000000000000000000000000000000000000000000 selenium,Se,73.922475910000002841115929186344146728515625000000000000000000,0.008938426836876709608015190156038443092256784439086914062500 selenium,Se,75.919213720000001899279595818370580673217773437500000000000000,0.093712506598838590798905556766840163618326187133789062500000 selenium,Se,76.919914259999998762395989615470170974731445312500000000000000,0.076302570747548426055573145276866853237152099609375000000000 selenium,Se,77.917309200000005375841283239424228668212890625000000000000000,0.237686167234566703143627819372341036796569824218750000000000 selenium,Se,79.916522900000003915010893251746892929077148437500000000000000,0.496053694549759227605534306348999962210655212402343750000000 selenium,Se,81.916700099999999906685843598097562789916992187500000000000000,0.087306634032410290746639702774700708687305450439453125000000 bromine,Br,78.918338099999999712963472120463848114013671875000000000000000,0.506898896176611657438115798868238925933837890625000000000000 bromine,Br,80.916290099999997664781403727829456329345703125000000000000000,0.493101103823388231539581738616107031702995300292968750000000 krypton,Kr,77.920365599999996675251168198883533477783203125000000000000000,0.003552948126957346328819165037771199422422796487808227539063 krypton,Kr,79.916378600000001597436494193971157073974609375000000000000000,0.022860666234272977725971998097520554438233375549316406250000 krypton,Kr,81.913483700000000453655957244336605072021484375000000000000000,0.115931407401451927463575941601447993889451026916503906250000 krypton,Kr,82.914127199999995809776009991765022277832031250000000000000000,0.115000220996773441783922464765055337920784950256347656250000 krypton,Kr,83.911497733000004473069566302001476287841796875000000000000000,0.569863179997571966950431487930472940206527709960937500000000 krypton,Kr,85.910610633000004554560291580855846405029296875000000000000000,0.172791577242972227423933873069472610950469970703125000000000 rubidium,Rb,84.911789742999999930361809674650430679321289062500000000000000,0.721691132354705722207199869444593787193298339843750000000000 rubidium,Rb,86.909180535999993821860698517411947250366210937500000000000000,0.278308867645294166770497668039752170443534851074218750000000 strontium,Sr,83.913419899999993845085555221885442733764648437500000000000000,0.005609775608975640752429381308274969342164695262908935546875 strontium,Sr,85.909261900000004175126377958804368972778320312500000000000000,0.098606055757769678349333730693615507334470748901367187500000 strontium,Sr,86.908878900000004819048626814037561416625976562500000000000000,0.070007199712011511372189431767765199765563011169433593750000 strontium,Sr,87.905613900000005855872586835175752639770507812500000000000000,0.825776968921243081922511919401586055755615234375000000000000 yttrium,Y,88.905842000000006919435691088438034057617187500000000000000000,1.000000000000000000000000000000000000000000000000000000000000 zirconium,Zr,89.904702000000000339241523761302232742309570312500000000000000,0.514422711621750239352479638910153880715370178222656250000000 zirconium,Zr,90.905642000000000280124368146061897277832031250000000000000000,0.112234410554393593262290096390643157064914703369140625000000 zirconium,Zr,91.905032000000005609763320535421371459960937500000000000000000,0.171550886397901253266340404479706194251775741577148437500000 zirconium,Zr,93.906311999999999784449755679816007614135742187500000000000000,0.173788376250214926521664438041625544428825378417968750000000 zirconium,Zr,95.908271999999996637598087545484304428100585937500000000000000,0.028003615175739928616627238966430013533681631088256835937500 niobium,Nb,92.906372000000004618414095602929592132568359375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 molybdenum,Mo,91.906808600000005071706254966557025909423828125000000000000000,0.145308494342837241086741073559096548706293106079101562500000 molybdenum,Mo,93.905085299999996095721144229173660278320312500000000000000000,0.091496458524138415957516201615362660959362983703613281250000 molybdenum,Mo,94.905839299999996683254721574485301971435546875000000000000000,0.158387558641321063435114524509117472916841506958007812500000 molybdenum,Mo,95.904676300000005539914127439260482788085937500000000000000000,0.166690329831184980147185115129104815423488616943359375000000 molybdenum,Mo,96.906018299999999499050318263471126556396484375000000000000000,0.095999792030779435014764544575882609933614730834960937500000 molybdenum,Mo,97.905405299999998192106431815773248672485351562500000000000000,0.243900902666405350327494261364336125552654266357421875000000 molybdenum,Mo,99.907472799999993640085449442267417907714843750000000000000000,0.098216463963333416886669624545902479439973831176757812500000 ruthenium,Ru,95.907590299999995409052644390612840652465820312500000000000000,0.055402974808013198682044020415560225956141948699951171875000 ruthenium,Ru,97.905296000000006984009814914315938949584960937500000000000000,0.018726273471579152340993346115283202379941940307617187500000 ruthenium,Ru,98.905934799999997153463482391089200973510742187500000000000000,0.127588609866636532030881312493875157088041305541992187500000 ruthenium,Ru,99.904214800000005425317795015871524810791015625000000000000000,0.126054915071900669465421174209041055291891098022460937500000 ruthenium,Ru,100.905577899999997271152096800506114959716796875000000000000000,0.170586053375378299268305681835045106709003448486328125000000 ruthenium,Ru,101.904344899999998119710653554648160934448242187500000000000000,0.315451225206183960558803391904802992939949035644531250000000 ruthenium,Ru,103.905432000000004677531251218169927597045898437500000000000000,0.186189948200308125203505937861336860805749893188476562500000 rhodium,Rh,102.905501999999998474777385126799345016479492187500000000000000,1.000000000000000000000000000000000000000000000000000000000000 palladium,Pd,101.905602000000001794433046597987413406372070312500000000000000,0.010207550187954890497099569302008603699505329132080078125000 palladium,Pd,103.904031099999997422855813056230545043945312500000000000000000,0.111463248820283120088525663504697149619460105895996093750000 palladium,Pd,104.905080900000001520311343483626842498779296875000000000000000,0.223336399264176588275176982278935611248016357421875000000000 palladium,Pd,105.903480900000005249239620752632617950439453125000000000000000,0.273264416540030363744762098576757125556468963623046875000000 palladium,Pd,107.903892900000002441629476379603147506713867187500000000000000,0.264546508837878890929573572066146880388259887695312500000000 palladium,Pd,109.905172600000000215914042200893163681030273437500000000000000,0.117181876349676070137029171291942475363612174987792968750000 silver,Ag,106.905091999999996232872945256531238555908203125000000000000000,0.518389668985958174118877650471404194831848144531250000000000 silver,Ag,108.904755100000002698834578040987253189086914062500000000000000,0.481610331014041714858819887012941762804985046386718750000000 cadmium,Cd,105.906460899999999014653440099209547042846679687500000000000000,0.012567197514954164816458614950533956289291381835937500000000 cadmium,Cd,107.904183900000006701702659483999013900756835937500000000000000,0.008928009053980960965657409644791187020018696784973144531250 cadmium,Cd,109.903007400000007010021363385021686553955078125000000000000000,0.124890149496662231087817929164884844794869422912597656250000 cadmium,Cd,110.904183399999993753226590342819690704345703125000000000000000,0.127983459688489453753845737082883715629577636718750000000000 cadmium,Cd,111.902763399999997773193172179162502288818359375000000000000000,0.241267197414976458658131264201074372977018356323242187500000 cadmium,Cd,112.904408300000000053842086344957351684570312500000000000000000,0.122184752800125570604272695618419675156474113464355468750000 cadmium,Cd,113.903365300000004367575456853955984115600585937500000000000000,0.287277937020044504823346187549759633839130401611328125000000 cadmium,Cd,115.904763200000004985668056178838014602661132812500000000000000,0.074901297010766587636254598692175932228565216064453125000000 indium,In,112.904062699999997221311787143349647521972656250000000000000000,0.042954845418549769675564675708301365375518798828125000000000 indium,In,114.903878789000003735054633580148220062255859375000000000000000,0.957045154581450119302132861776044592261314392089843750000000 tin,Sn,111.904824399999995421239873394370079040527343750000000000000000,0.009707379007667929146641050408561568474397063255310058593750 tin,Sn,113.902783700000000521868059877306222915649414062500000000000000,0.006608215781738930282018795736576066701672971248626708984375 tin,Sn,114.903344709999998940475052222609519958496093750000000000000000,0.003409079548521898664348306340343697229400277137756347656250 tin,Sn,115.901743100000004460525815375149250030517578125000000000000000,0.145370749897527656857576516813423950225114822387695312500000 tin,Sn,116.902954300000004650428309105336666107177734375000000000000000,0.076859248003039171148742525474517606198787689208984375000000 tin,Sn,117.901607299999994893369148485362529754638671875000000000000000,0.242144620952342848330118840749491937458515167236328125000000 tin,Sn,118.903311599999994996323948726058006286621093750000000000000000,0.085916802463334898676272644024720648303627967834472656250000 tin,Sn,119.902202700000003687819116748869419097900390625000000000000000,0.325722055045137792728127124064485542476177215576171875000000 tin,Sn,121.903441999999998301973391789942979812622070312500000000000000,0.046317494276545329023875297025369945913553237915039062500000 tin,Sn,123.905277799999993249002727679908275604248046875000000000000000,0.057944355024143474885978122301821713335812091827392578125000 antimony,Sb,120.903812000000002058186510112136602401733398437500000000000000,0.572091349038115315472907695948379114270210266113281250000000 antimony,Sb,122.904212000000001125954440794885158538818359375000000000000000,0.427908650961884573504789841535966843366622924804687500000000 tellurium,Te,119.904061999999996146470948588103055953979492187500000000000000,0.000909764371027903685079651907585684966761618852615356445313 tellurium,Te,121.903041000000001758962753228843212127685546875000000000000000,0.025505394102927340937991829150632838718593120574951171875000 tellurium,Te,122.904270999999994273821357637643814086914062500000000000000000,0.008927687728878220055350745099076448241248726844787597656250 tellurium,Te,123.902821000000002982233127113431692123413085937500000000000000,0.047401722953754971134898710261040832847356796264648437500000 tellurium,Te,124.904431000000002427441359031945466995239257812500000000000000,0.070696689557404629455916733604681212455034255981445312500000 tellurium,Te,125.903311000000002195520210079848766326904296875000000000000000,0.188376210561464557668998054396070074290037155151367187500000 tellurium,Te,127.904461699999998813837009947746992111206054687500000000000000,0.317407791382032011817670991149498149752616882324218750000000 tellurium,Te,129.906222759000002042739652097225189208984375000000000000000000,0.340774739342510235573513455165084451436996459960937500000000 iodine,I,126.904472999999995863618096336722373962402343750000000000000000,1.000000000000000000000000000000000000000000000000000000000000 xenon,Xe,123.905891999999994368408806622028350830078125000000000000000000,0.000952296533640617525774685336870106766582466661930084228516 xenon,Xe,125.904302999999998746716300956904888153076171875000000000000000,0.000890196759683794711613680217254795934422872960567474365234 xenon,Xe,127.903531799999996110273059457540512084960937500000000000000000,0.019102830465697103606848017420816177036613225936889648437500 xenon,Xe,128.904780864000002793545718304812908172607421875000000000000000,0.264005869018636762923790683998959138989448547363281250000000 xenon,Xe,129.903509409999998069906723685562610626220703125000000000000000,0.040709981815666186621971434078659513033926486968994140625000 xenon,Xe,130.905084200000004557296051643788814544677734375000000000000000,0.212323527142361190289676642350968904793262481689453125000000 xenon,Xe,131.904155094000003600740456022322177886962890625000000000000000,0.269085350529324029977829013660084456205368041992187500000000 xenon,Xe,133.905395700000013903263607062399387359619140625000000000000000,0.104356830141138279266499466757522895932197570800781250000000 xenon,Xe,135.907214487999993934863596223294734954833984375000000000000000,0.088573117593851946605099101361702196300029754638671875000000 caesium,Cs,132.905451967000004742658347822725772857666015625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 barium,Ba,129.906321999999988747731549665331840515136718750000000000000000,0.001060985146207953045902061539607075246749445796012878417969 barium,Ba,131.905061799999998584098648279905319213867187500000000000000000,0.001010985846198153050023993415607037604786455631256103515625 barium,Ba,133.904508200000009310315363109111785888671875000000000000000000,0.024171461599537605313692267827718751505017280578613281250000 barium,Ba,134.905688199999985954491421580314636230468750000000000000000000,0.065920277116120362670415033790050074458122253417968750000000 barium,Ba,135.904576200000008157076081261038780212402343750000000000000000,0.078541300421794094099858796198532218113541603088378906250000 barium,Ba,136.905827200000004495450411923229694366455078125000000000000000,0.112320827508414877726750091824214905500411987304687500000000 barium,Ba,137.905247199999990925789461471140384674072265625000000000000000,0.716974162361726841119491382414707913994789123535156250000000 lanthanum,La,137.907123000000012780219549313187599182128906250000000000000000,0.000888171872103250392010975744483403104823082685470581054688 lanthanum,La,138.906362000000001444277586415410041809082031250000000000000000,0.999111828127896672846475212281802669167518615722656250000000 cerium,Ce,135.907129300000008242932381108403205871582031250000000000000000,0.001851973331584025024912354417949700291501358151435852050781 cerium,Ce,137.905998000000010961230145767331123352050781250000000000000000,0.002511963827720880421123794690174690913408994674682617187500 cerium,Ce,139.905441999999993640813045203685760498046875000000000000000000,0.884492463308528265031327464384958148002624511718750000000000 cerium,Ce,141.909252000000009275026968680322170257568359375000000000000000,0.111143599532166723053983048430382041260600090026855468750000 praseodymium,Pr,140.907661999999987756382324732840061187744140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 neodymium,Nd,141.907732000000009975337889045476913452148437500000000000000000,0.271519166958828106483991859931848011910915374755859375000000 neodymium,Nd,142.909821999999991248841979540884494781494140625000000000000000,0.121740433020292235233306143982190405949950218200683593750000 neodymium,Nd,143.910091999999991685399436391890048980712890625000000000000000,0.237977663997580829446931716120161581784486770629882812500000 neodymium,Nd,144.912581999999986237526172772049903869628906250000000000000000,0.082929723850915446070608538775559281930327415466308593750000 neodymium,Nd,145.913121999999987110641086474061012268066406250000000000000000,0.171890140355501652713599014532519504427909851074218750000000 neodymium,Nd,147.916901999999993222445482388138771057128906250000000000000000,0.057561075412857647115583148433870519511401653289794921875000 neodymium,Nd,149.920902000000012321834219619631767272949218750000000000000000,0.056381796404024006608146635244338540360331535339355468750000 samarium,Sm,143.912012000000004263711161911487579345703125000000000000000000,0.030772522277086666181444840617587033193558454513549804687500 samarium,Sm,146.914902000000012094460544176399707794189453125000000000000000,0.149881578776357327065227309503825381398200988769531250000000 samarium,Sm,147.914831999999989875504979863762855529785156250000000000000000,0.112382691006085513873991033051424892619252204895019531250000 samarium,Sm,148.917192000000000007275957614183425903320312500000000000000000,0.138246406123312015612469849656918086111545562744140625000000 samarium,Sm,149.917282000000000152795109897851943969726562500000000000000000,0.073792068527347848272412988990254234522581100463867187500000 samarium,Sm,151.919742000000013604221749119460582733154296875000000000000000,0.267451009404714612482933944193064235150814056396484375000000 samarium,Sm,153.922222000000004982211976312100887298583984375000000000000000,0.227473723885095902019770619517657905817031860351562500000000 europium,Eu,150.919861999999994850440998561680316925048828125000000000000000,0.478103065570820051632949798658955842256546020507812500000000 europium,Eu,152.921242000000006555637810379266738891601562500000000000000000,0.521896934429179837344747738825390115380287170410156250000000 gadolinium,Gd,151.919802000000004227331373840570449829101562500000000000000000,0.002009636255837693018938550082452820788603276014328002929688 gadolinium,Gd,153.920872000000002799424692057073116302490234375000000000000000,0.021826049485043207132317633067941642366349697113037109375000 gadolinium,Gd,154.922631999999993013261700980365276336669921875000000000000000,0.147985214676143617129611129712429828941822052001953125000000 gadolinium,Gd,155.922132000000004836692824028432369232177734375000000000000000,0.204672954195290635048820604424690827727317810058593750000000 gadolinium,Gd,156.923971999999992021912476047873497009277343750000000000000000,0.156491675006823760529783839956508018076419830322265625000000 gadolinium,Gd,157.924112000000008038114174269139766693115234375000000000000000,0.248435033258980114689862261911912355571985244750976562500000 gadolinium,Gd,159.927062000000006491973181255161762237548828125000000000000000,0.218579437121880937322515592313720844686031341552734375000000 terbium,Tb,158.925352000000003727109287865459918975830078125000000000000000,1.000000000000000000000000000000000000000000000000000000000000 dysprosium,Dy,155.924282000000005155015969648957252502441406250000000000000000,0.000562985756460361477619691594753703611786477267742156982422 dysprosium,Dy,157.924421999999992749508237466216087341308593750000000000000000,0.000952975889709990254573812595850768047966994345188140869141 dysprosium,Dy,159.925202000000012958480510860681533813476562500000000000000000,0.023291210732368467645203580218549177516251802444458007812500 dysprosium,Dy,160.926941999999996824044501408934593200683593750000000000000000,0.188889421097646226233024435714469291269779205322265625000000 dysprosium,Dy,161.926812000000012403688742779195308685302734375000000000000000,0.254747154896981076177553404704667627811431884765625000000000 dysprosium,Dy,162.928741999999999734427547082304954528808593750000000000000000,0.248957901365095435330943018925609067082405090332031250000000 dysprosium,Dy,163.929181999999997287886799313127994537353515625000000000000000,0.282598350261738351374418698469526134431362152099609375000000 holmium,Ho,164.930331999999992831362760625779628753662109375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 erbium,Er,161.928791999999987183400662615895271301269531250000000000000000,0.001395973476503946332158423437874716910300776362419128417969 erbium,Er,163.929212000000006810296326875686645507812500000000000000000000,0.016012695758780580435054474719436257146298885345458984375000 erbium,Er,165.930302000000011730662663467228412628173828125000000000000000,0.335027234482544788995994622382568195462226867675781250000000 erbium,Er,166.932051999999998770363163203001022338867187500000000000000000,0.228686654953555862368475004586798604577779769897460937500000 erbium,Er,167.932381999999989830030244775116443634033203125000000000000000,0.269776674243189351631855288360384292900562286376953125000000 erbium,Er,169.935472000000004300090949982404708862304687500000000000000000,0.149100767085425356395234075534972362220287322998046875000000 thulium,Tm,168.934222000000005436959327198565006256103515625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 ytterbium,Yb,167.933891999999985955582815222442150115966796875000000000000000,0.001232929969577727796758992440118163358420133590698242187500 ytterbium,Yb,169.934772000000009484210750088095664978027343750000000000000000,0.029822206098693591902470956256365752778947353363037109375000 ytterbium,Yb,170.936331999999993058736436069011688232421875000000000000000000,0.140905996539396560773838018576498143374919891357421875000000 ytterbium,Yb,171.936392000000012103555491194128990173339843750000000000000000,0.216800685721051017429417129278590437024831771850585937500000 ytterbium,Yb,172.938221999999996114638634026050567626953125000000000000000000,0.161027253651992552363481081556528806686401367187500000000000 ytterbium,Yb,173.938872000000003481545718386769294738769531250000000000000000,0.320249909805123023076589561242144554853439331054687500000000 ytterbium,Yb,175.942581999999987374394549988210201263427734375000000000000000,0.129961018214165419104588750087714288383722305297851562500000 lutetium,Lu,174.940782000000012885720934718847274780273437500000000000000000,0.974008767577204226384424146090168505907058715820312500000000 lutetium,Lu,175.942691999999993868186720646917819976806640625000000000000000,0.025991232422795697287742910930319339968264102935791015625000 hafnium,Hf,173.940052000000008547431207261979579925537109375000000000000000,0.001609652315099938373749166586890169128309935331344604492188 hafnium,Hf,175.941412000000013904355000704526901245117187500000000000000000,0.052668623577307296934613134453684324398636817932128906250000 hafnium,Hf,176.943231999999994741301634348928928375244140625000000000000000,0.185969830516608397585898160286888014525175094604492187500000 hafnium,Hf,177.943712000000004991306923329830169677734375000000000000000000,0.272821070648739838482299546740250661969184875488281250000000 hafnium,Hf,178.945821999999992613084032200276851654052734375000000000000000,0.136190582834107815068946933934057597070932388305664062500000 hafnium,Hf,179.946562000000000125510268844664096832275390625000000000000000,0.350740240108136591690168870627530850470066070556640625000000 tantalum,Ta,179.947462000000001580701791681349277496337890625000000000000000,0.000120131992311552486551486096377772128107608295977115631104 tantalum,Ta,180.948002000000002453816705383360385894775390625000000000000000,0.999879868007688354936135510797612369060516357421875000000000 tungsten,W,179.946711999999990894139045849442481994628906250000000000000000,0.001209872963338849303702171589236513682408258318901062011719 tungsten,W,181.948204699999990907599567435681819915771484375000000000000000,0.264988176241494621798722164385253563523292541503906250000000 tungsten,W,182.950223700000009330324246548116207122802734375000000000000000,0.143124971877952811283307710255030542612075805664062500000000 tungsten,W,183.950931700000012369855539873242378234863281250000000000000000,0.306387829277925793913794905165559612214565277099609375000000 tungsten,W,185.954362000000003263266989961266517639160156250000000000000000,0.284289149639287863635672692907974123954772949218750000000000 rhenium,Re,184.952955900000006295158527791500091552734375000000000000000000,0.374005039798408045470523575204424560070037841796875000000000 rhenium,Re,186.955750999999992245648172684013843536376953125000000000000000,0.625994960201591843507173962279921397566795349121093750000000 osmium,Os,183.952489100000008193092071451246738433837890625000000000000000,0.000209947723016968765524098428087995671376120299100875854492 osmium,Os,185.953841000000011263182386755943298339843750000000000000000000,0.015926034417430057904541129687459033448249101638793945312500 osmium,Os,186.955750999999992245648172684013843536376953125000000000000000,0.019615115836156795520173190539026109036058187484741210937500 osmium,Os,187.955840999999992391167324967682361602783203125000000000000000,0.132457018202467580181291850749403238296508789062500000000000 osmium,Os,188.958142000000009375071385875344276428222656250000000000000000,0.161519781574387955025429164379602298140525817871093750000000 osmium,Os,189.958441999999990912328939884901046752929687500000000000000000,0.262554623898649197588639481182326562702655792236328125000000 osmium,Os,191.961481999999989511707099154591560363769531250000000000000000,0.407717478347891348899878494194126687943935394287109375000000 iridium,Ir,190.960591999999991230652085505425930023193359375000000000000000,0.373050779688124722888176165724871680140495300292968750000000 iridium,Ir,192.962921999999991840013535693287849426269531250000000000000000,0.626949220311875166089521371759474277496337890625000000000000 platinum,Pt,189.959934000000004061803338117897510528564453125000000000000000,0.000121987349911814132899338936066868654961581341922283172607 platinum,Pt,191.961041999999991958247846923768520355224609375000000000000000,0.007821588901230941415221309398475568741559982299804687500000 platinum,Pt,193.962681699999990314609021879732608795166015625000000000000000,0.328605923565726210089366077227168716490268707275390625000000 platinum,Pt,194.964792700000003833338269032537937164306640625000000000000000,0.337788971283677852408544595164130441844463348388671875000000 platinum,Pt,195.964952699999997776103555224835872650146484375000000000000000,0.252107856415289710572125159160350449383258819580078125000000 platinum,Pt,197.967892000000006191839929670095443725585937500000000000000000,0.073553672484163390432598816914833150804042816162109375000000 gold,Au,196.966569600000013906537787988781929016113281250000000000000000,1.000000000000000000000000000000000000000000000000000000000000 mercury,Hg,195.965832000000006019035936333239078521728515625000000000000000,0.001509815802472098391837085351596670079743489623069763183594 mercury,Hg,197.966769300000009934592526406049728393554687500000000000000000,0.099707835644051417967048678292485419660806655883789062500000 mercury,Hg,198.968281300000001010630512610077857971191406250000000000000000,0.168701418426951910145561441822792403399944305419921875000000 mercury,Hg,199.968327299999998558632796630263328552246093750000000000000000,0.230990819120067331082779560347262304276227951049804687500000 mercury,Hg,200.970303599999994048630469478666782379150390625000000000000000,0.131793921141620695713925215386552736163139343261718750000000 mercury,Hg,201.970643599999988282434060238301753997802734375000000000000000,0.298589572072207154462830658303573727607727050781250000000000 mercury,Hg,203.973494299999998702332959510385990142822265625000000000000000,0.068706617792629293139938795320631470531225204467773437500000 thallium,Tl,202.972345100000012507734936662018299102783203125000000000000000,0.295204095918081610427918803907232359051704406738281250000000 thallium,Tl,204.974428100000011454540072008967399597167968750000000000000000,0.704795904081918278549778733577113598585128784179687500000000 lead,Pb,203.973044899999990775540936738252639770507812500000000000000000,0.014094362255097959285565778486670751590281724929809570312500 lead,Pb,205.974466900000010127769201062619686126708984375000000000000000,0.241003598560575765796798464180028531700372695922851562500000 lead,Pb,206.975897900000006757181836292147636413574218750000000000000000,0.221011595361855245345239495691203046590089797973632812500000 lead,Pb,207.976653900000002295200829394161701202392578125000000000000000,0.523890443822470963652904174523428082466125488281250000000000 bismuth,Bi,208.980401000000000522049958817660808563232421875000000000000000,1.000000000000000000000000000000000000000000000000000000000000 uranium,U,234.040952000000004318280844017863273620605468750000000000000000,0.000054599923560107009460132254652364736102754250168800354004 uranium,U,235.043932000000012294549378566443920135498046875000000000000000,0.007204689913434121108226637630878030904568731784820556640625 uranium,U,238.050792000000001280568540096282958984375000000000000000000000,0.992740710163005690702675565262325108051300048828125000000000 thorium,Th,232.038061999999996487531461752951145172119140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 protactinium,Pa,231.035881999999986646798788569867610931396484375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/leucine.svg000664 001750 001750 00000003204 15100504560 034617 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/lysine.svg000664 001750 001750 00000003742 15100504560 034505 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/methionine.svg000664 001750 001750 00000003775 15100504560 035270 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/modification_dictionary000664 001750 001750 00000003227 15100504560 037215 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the modification_dictionary file where the correspondences # between the name of each modification and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. Also, the graphical operation that is to # be performed upon chemical modification of a monomer is listed ('T' # for transparent and 'O' for opaque). See the manual for details. # The format of the file is like this : # ------------------------------------- # Phosphorylation%T%phospho.svg # where Phosphorylation is the name of the modification. T indicates # that the visual rendering of the modification is a transparent # process (O indicates that the visual rendering of the modification # is a full image replacement 'O' like opaque). phospho.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Phosphorylation%T%phospho.svg Sulphation%T%sulpho.svg AmidationAsp%O%asparagine.svg Acetylation%T%acetyl.svg Glutamylation%T%glutamylation.svg Glycylation%T%glycylation.svg AmidationGlu%O%glutamine.svg Oxidation%T%oxidation.svg SulfideBond%T%sulfbond.svg ProtonLoss%T%protonloss.svg Hydroxylation%T%hydroxylation.svg tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/monomer_dictionary000664 001750 001750 00000002377 15100504560 036231 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the monomer_dictionary file where the correspondences # between the codes of each monomer and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. # The format of the file is like this : # ------------------------------------- # A%alanine.svg # where A is the monomer code and alanine.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. A%alanine.svg C%cysteine.svg D%aspartate.svg E%glutamate.svg F%phenylalanine.svg G%glycine.svg H%histidine.svg I%isoleucine.svg K%lysine.svg L%leucine.svg M%methionine.svg N%asparagine.svg P%proline.svg Q%glutamine.svg R%arginine.svg S%serine.svg T%threonine.svg V%valine.svg W%tryptophan.svg Y%tyrosine.svg O%diaminopimelic.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/oxidation.svg000664 001750 001750 00000005727 15100504560 035205 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/pdb-code-conversion.dic000664 001750 001750 00000000506 15100504560 036716 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# Converts from code on the left of '>' to code on the right. # Number of letters allowed in each code is # described with syntax 1>3 and that line should be the first # non-comment line in this file. 3>1 ALA>A CYS>C ASP>D GLU>E PHE>F GLY>G HIS>H ILE>I LYS>K LEU>L MET>M ASN>N PRO>P GLN>Q ARG>R SER>S THR>T VAL>V TRP>W TYR>Y tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/phenylalanine.svg000664 001750 001750 00000003344 15100504560 035750 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/phospho.svg000664 001750 001750 00000002365 15100504560 034662 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/pka_ph_pi.xml000664 001750 001750 00000031470 15100504560 035134 0ustar00rusconirusconi000000 000000 ]> A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped D N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral COOH 3.65 FALSE never_trapped MONOMER_MODIF AmidationAsp LOST E N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral COOH 4.25 FALSE never_trapped MONOMER_MODIF AmidationGlu LOST F N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped G N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped H N-term NH2 9.6 TRUE left_trapped C-term COOH 2.36 FALSE right_trapped In-ring NH+ 6 TRUE never_trapped I N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped K N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral NH2 10.53 TRUE never_trapped MONOMER_MODIF Acetylation LOST L N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped M N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped N N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped P N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Q N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped R N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral guanidinium 12.48 TRUE never_trapped S N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral alcohol 13 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST T N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral alcohol 13 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST V N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped W N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Y N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral phenol 10.1 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/proline.svg000664 001750 001750 00000003677 15100504560 034661 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/protein-1-letter.xml000664 001750 001750 00000025624 15100504560 036242 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> protein-1-letter "N-term amine"+H "C-term carboxylic acid"+OH 1 +H 1 1 Alanine A C3H5N1O1 Cysteine C C3H5N1O1S1 Aspartate D C4H5N1O3 Glutamate E C5H7N1O3 Phenylalanine F C9H9N1O1 Glycine G C2H3N1O1 Histidine H C6H7N3O1 Isoleucine I C6H11N1O1 Lysine K C6H12N2O1 Leucine L C6H11N1O1 Methionine M C5H9N1O1S1 Asparagine N C4H6N2O2 DiaminoPimelic O C7H12N2O3 Proline P C5H7N1O1 Glutamine Q C5H8N2O2 Arginine R C6H12N4O1 Serine S C3H5N1O2 Threonine T C4H7N1O2 Valine V C5H9N1O1 Tryptophan W C11H10N2O1 Tyrosine Y C9H9N1O2 Acetylation C2H2O1 ;K; 1 AmidationAsp H1N1-O1 ;D; 1 AmidationGlu H1N1-O1 ;E; 1 Carbamidomethylation C2H3N1O1 ;C; 1 Carbamylation C1H1N1O1 ;K; 1 CarboxyMethylation C2H2O2 ;C; 1 Chromo-H -H1 ;G; 1 Chromo-H3 -H3 ;Y;F;W; 1 Chromo-O -O1 ;T;S;G; 1 DTNB (adsorbed) C14H8N2O8S2 ;C; 1 Dehydroalanine -H2S1 ;C; 1 Dehydroxylation -H1O1 ;D;E; 1 Formylation C1O1 * 1 GlcNAcMurNAc(R) C19H32N2O12 * 1 Glutamylation C5H7N1O3 ;E; 6 Glycylation C2H3N1O1 ;E; 34 Hydroxylation H1O1 ;F; 1 LysMethylation C1H2 ;K; 3 Methylation C1H2 ;K;R; 2 Oxidation O1 ;M;Y; 1 Phosphorylation H1O3P1 ;S;T;Y; 1 ProtonLoss -H1 ;C;Y;K; 1 SPITC C7H5N1O3S2 * 1 SulfideBond -H2 ;C; 1 Sulphation O3S1 ;S;T;Y; 1 TNB C7H3N1O4S1 ;C; 1 CFP-chromophore Chromo-O Chromo-H3 Chromo-H DisulfideBond ProtonLoss ProtonLoss Chymotrypsin W/;V/ CyanogenBromide M/ Homoseryl M -CH2S+O EndoAspN /D EndoAspN+GluN /D;/E EndoAspN-Trypsin /D;K/;R/;-K/P EndoLysC K/ GluC E/ Trypsin K/;R/;-K/P a LE -C1O1H1 0 b LE -H 0 c LE +N1H2 0 that's just a comment imm NE -C1O1 0 x RE +C1O1-H1 0 y RE +H1 0 z RE -N1H1 0 Typically in ECD of protonated ions tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/protonloss.svg000664 001750 001750 00000004266 15100504560 035347 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/serine.svg000664 001750 001750 00000005123 15100504560 034462 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/sulfbond.svg000664 001750 001750 00000011620 15100504560 035010 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/sulpho.svg000664 001750 001750 00000006334 15100504560 034514 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/threonine.svg000664 001750 001750 00000003612 15100504560 035171 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/tryptophan.svg000664 001750 001750 00000003443 15100504560 035331 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/tyrosine.svg000664 001750 001750 00000003310 15100504560 035045 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-1-letter/valine.svg000664 001750 001750 00000003240 15100504560 034451 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/000775 001750 001750 00000000000 15100507137 032643 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/acetyl.svg000664 001750 001750 00000006234 15100504560 034647 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/alanine.svg000664 001750 001750 00000004304 15100504560 034771 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/arginine.svg000664 001750 001750 00000004366 15100504560 035166 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/asparagine.svg000664 001750 001750 00000004523 15100504560 035420 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/aspartate.svg000664 001750 001750 00000005060 15100504560 035267 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/chemPad.conf000664 001750 001750 00000014633 15100504560 035057 0ustar00rusconirusconi000000 000000 # This is the layout for the chempad of the molecular calculator 'extremecalc' # in the massXpert software program. # Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Filippo Rusconi. # GNU General Public License. # The format of this file allows the definition of colors # according to the following syntax: # # color%OneColor%xxx,yyy,zzz # # with xxx, yyy and zzz numerical values in range [0,255] for the red, # green and blue component of the color respectively. # # Available colors are: white, black, red, darkRed, green, darkGreen, # blue, darkBlue, cyan, darkCyan, magenta, darkMagenta, yellow, # darkYellow, gray, darkGray, lightGray, please, see # http://www.w3.org/TR/SVG/types.html#ColorKeywords for a list of # commonly used colors associated to their rgb value. # # A separator might be used between sections of buttons. Its syntax is: # Note the double && that acts as an escape of the &. # # chempadsep%Hexoses && Fucose%[midnightblue] # # The color specification (%[midnightblue]) is not compulsory # General button syntax is like the example below: # # chempadkey%hydrate%+H2O1%adds a water molecule%[red,blue] # # Each line is divided into 4 compulsory elements: # 'chempadkey' starts the line # 'hydrate' is the label of the button # '+H2O1' is the formula that is applied when the button gets clicked # 'adds a water molecule' is the text to display in the tooltip # '[red,blue]' are the background and foreground colors, respectively # # The colors specification (last %[color,color]) is not compulsory # # The geometry is specified by the number of columns of buttons: # chempad_columns%3 would specify that all the buttons in the # chemical pad should be laid out using three columns and as # many rows as necessary to display them all. chempad_columns%3 color%aliceblue%240,248,255 color%antiquewhite%250,235,215 color%aqua%0,255,255 color%aquamarine%127,255,212 color%azure%240,255,255 color%beige%245,245,220 color%bisque%255,228,196 color%black%0,0,0 color%blanchedalmond%255,235,205 color%blue%0,0,255 color%blueviolet%138,43,226 color%brown%165,42,42 color%burlywood%222,184,135 color%cadetblue%95,158,160 color%chartreuse%127,255,0 color%chocolate%210,105,30 color%coral%255,127,80 color%cornflowerblue%100,149,237 color%cornsilk%255,248,220 color%crimson%220,20,60 color%cyan%0,255,255 color%darkblue%0,0,139 color%darkcyan%0,139,139 color%darkgoldenrod%184,134,11 color%darkgray%169,169,169 color%darkgreen%0,100,0 color%darkgrey%169,169,169 color%darkkhaki%189,183,107 color%darkmagenta%139,0,139 color%darkolivegreen%85,107,47 color%darkorange%255,140,0 color%darkorchid%153,50,204 color%darkred%139,0,0 color%darksalmon%233,150,122 color%darkseagreen%143,188,143 color%darkslateblue%72,61,139 color%darkslategray%47,79,79 color%darkslategrey%47,79,79 color%darkturquoise%0,206,209 color%darkviolet%148,0,211 color%deeppink%255,20,147 color%deepskyblue%0,191,255 color%dimgray%105,105,105 color%dimgrey%105,105,105 color%dodgerblue%30,144,255 color%firebrick%178,34,34 color%floralwhite%255,250,240 color%forestgreen%34,139,34 color%fuchsia%255,0,255 color%gainsboro%220,220,220 color%ghostwhite%248,248,255 color%gold%255,215,0 color%goldenrod%218,165,32 color%gray%128,128,128 color%grey%128,128,128 color%green%0,128,0 color%greenyellow%173,255,47 color%honeydew%240,255,240 color%hotpink%255,105,180 color%indianred%205,92,92 color%indigo%75,0,130 color%ivory%255,255,240 color%khaki%240,230,140 color%lavender%230,230,250 color%lavenderblush%255,240,245 color%lawngreen%124,252,0 color%lemonchiffon%255,250,205 color%lightblue%173,216,230 color%lightcoral%240,128,128 color%lightcyan%224,255,255 color%lightgoldenrodyellow%250,250,210 color%lightgray%211,211,211 color%lightgreen%144,238,144 color%lightgrey%211,211,211 color%lightpink%255,182,193 color%lightsalmon%255,160,122 color%lightseagreen%32,178,170 color%lightskyblue%135,206,250 color%lightslategray%119,136,153 color%lightslategrey%119,136,153 color%lightsteelblue%176,196,222 color%lightyellow%255,255,224 color%lime%0,255,0 color%limegreen%50,205,50 color%linen%250,240,230 color%magenta%255,0,255 color%maroon%128,0,0 color%mediumaquamarine%102,205,170 color%mediumblue%0,0,205 color%mediumorchid%186,85,211 color%mediumpurple%147,112,219 color%mediumseagreen%60,179,113 color%mediumslateblue%123,104,238 color%mediumspringgreen%0,250,154 color%mediumturquoise%72,209,204 color%mediumvioletred%199,21,133 color%midnightblue%25,25,112 color%mintcream%245,255,250 color%mistyrose%255,228,225 color%moccasin%255,228,181 color%navajowhite%255,222,173 color%navy%0,0,128 color%oldlace%253,245,230 color%olive%128,128,0 color%olivedrab%107,142,35 color%orange%255,165,0 color%orangered%255,69,0 color%orchid%218,112,214 color%palegoldenrod%238,232,170 color%palegreen%152,251,152 color%paleturquoise%175,238,238 color%palevioletred%219,112,147 color%papayawhip%255,239,213 color%peachpuff%255,218,185 color%peru%205,133,63 color%pink%255,192,203 color%plum%221,160,221 color%powderblue%176,224,230 color%purple%128,0,128 color%red%255,0,0 color%rosybrown%188,143,143 color%royalblue%65,105,225 color%saddlebrown%139,69,19 color%salmon%250,128,114 color%sandybrown%244,164,96 color%seagreen%46,139,87 color%seashell%255,245,238 color%sienna%160,82,45 color%silver%192,192,192 color%skyblue%135,206,235 color%slateblue%106,90,205 color%slategray%112,128,144 color%slategrey%112,128,144 color%snow%255,250,250 color%springgreen%0,255,127 color%steelblue%70,130,180 color%tan%210,180,140 color%teal%0,128,128 color%thistle%216,191,216 color%tomato%255,99,71 color%turquoise%64,224,208 color%violet%238,130,238 color%wheat%245,222,179 color%white%255,255,255 color%whitesmoke%245,245,245 color%yellow%255,255,0 color%yellowgreen%154,205,50 chempad_columns%3 chempadgroup%Generic chempadkey%protonate%+H1%adds a proton chempadkey%hydrate%+H2O1%adds a water molecule chempadkey%0H-ylate%+O1H1%adds an hydroxyl group chempadkey%acetylate%-H1+C2H3O1%adds an acetyl group chempadkey%phosphorylate%-H+H2PO3%add a phosphate group chempadkey%sulfide bond%-H2%oxydizes with loss of hydrogen chempadgroup%Hexoses%[seagreen] chempadkey%Res-GlucNAc%+C8H14N1O6%residue GlcNAc%[lawngreen,black] chempadkey%Res-Lactoyl-MurNAc%+C11H17N1O6%residue MurNAc+Lactoyl%[lawngreen,black] chempadgroup%Step residues%[tomato] chempadkey%Res-mDAP%+C7H12N2O3%residue mDAP%[tomato,black] chempadkey%Res-iGlu%+C5H7N1O3%residue iGlu%[tomato,black] chempadkey%Res-Ala%+C3H5N1O1%residue Ala%[tomato,black] chempadkey%Res-iLys%+C6H12N2O2%residue iLys%[tomato,black] tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/cross_linker_dictionary000664 001750 001750 00000002204 15100504560 037424 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007,2008 Filippo Rusconi # This is the cross_linker_dictionary file where the correspondences # between the name of each cross-link and their graphic file (svg file # called "image") used to graphicallly render them in the sequence # editor are made. See the manual for details. # The format of the file is like this : # ------------------------------------- # DisulfideBond%disulfidebond.svg # where DisulfideBond is the name of the cross-link. disulfidebond.svg # is a resolution-independent svg file to be used to render the # cross-link vignette. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. DisulfideBond%disulfidebond-cross-link.svg CFP-chromophore%cfp-chromophore.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/cursor.svg000664 001750 001750 00000002144 15100504560 034677 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/cysteine.svg000664 001750 001750 00000005167 15100504560 035215 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/default-cross-link-vignette.svg000664 001750 001750 00000013740 15100504560 040640 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/default-modif-vignette.svg000664 001750 001750 00000017145 15100504560 037655 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/glutamate.svg000664 001750 001750 00000004000 15100504560 035257 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/glutamine.svg000664 001750 001750 00000004002 15100504560 035263 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/glycine.svg000664 001750 001750 00000004106 15100504560 035014 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/histidine.svg000664 001750 001750 00000004101 15100504560 035256 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/isoleucine.svg000664 001750 001750 00000003340 15100504560 035441 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/isotopic-data.dat000664 001750 001750 00000116230 15100504560 036016 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file contains isotopic data in a format that can accommodate # comments in the form of lines beginning with the '#' character. hydrogen,H,1.007825032269999976364260874106548726558685302734375000000000,0.999884290164307909520857720053754746913909912109375000000000 hydrogen,H,2.014101778190000135992931973305530846118927001953125000000000,0.000115709835692033314582735648023970043141162022948265075684 helium,He,3.016029322000000068015879151062108576297760009765625000000000,0.000001342999991941999914655050951672876635711872950196266174 helium,He,4.002603254140000288430201180744916200637817382812500000000000,0.999998657000008006612290500925155356526374816894531250000000 lithium,Li,6.015122887100000426130463893059641122817993164062500000000000,0.075933925285977116326208147256693337112665176391601562500000 lithium,Li,7.016003442999999784035480843158438801765441894531250000000000,0.924066074714022800407065005856566131114959716796875000000000 beryllium,Be,9.012183159999999304545781342312693595886230468750000000000000,1.000000000000000000000000000000000000000000000000000000000000 boron,B,10.012937300000000817590262158773839473724365234375000000000000,0.199480830670926506664741850727295968681573867797851562500000 boron,B,11.009305299999999405713424494024366140365600585937500000000000,0.800519169329073410068531302385963499546051025390625000000000 carbon,C,12.000000000000000000000000000000000000000000000000000000000000,0.989211941850466902614869013632414862513542175292968750000000 carbon,C,13.003354835199999683936766814440488815307617187500000000000000,0.010788058149533083507343178553128382191061973571777343750000 nitrogen,N,14.003074004200000146624915942084044218063354492187500000000000,0.996358014567941707717579902237048372626304626464843750000000 nitrogen,N,15.000108899400000694868140271864831447601318359375000000000000,0.003641985432058271465738386041266494430601596832275390625000 oxygen,O,15.994914620199999433225457323715090751647949218750000000000000,0.997567609729561044495937949250219389796257019042968750000000 oxygen,O,16.999131757600000725005884305573999881744384765625000000000000,0.000380998476006095935803702490218825005285907536745071411133 oxygen,O,17.999159613700001614233769942075014114379882812500000000000000,0.002051391794432822109073288885383590240962803363800048828125 fluorine,F,18.998403163700000817470936453901231288909912109375000000000000,1.000000000000000000000000000000000000000000000000000000000000 neon,Ne,19.992440181999999282425051205791532993316650390625000000000000,0.904766666333356561757739200402284041047096252441406250000000 neon,Ne,20.993846730000001343796611763536930084228515625000000000000000,0.002709810313278070148523823945652111433446407318115234375000 neon,Ne,21.991385120000000341633494826965034008026123046875000000000000,0.092523523353365264010328417043638182803988456726074218750000 sodium,Na,22.989769282000001027199687086977064609527587890625000000000000,1.000000000000000000000000000000000000000000000000000000000000 magnesium,Mg,23.985041701000000102794729173183441162109375000000000000000000,0.789876809855211581279377242026384919881820678710937500000000 magnesium,Mg,24.985837029999999003848643042147159576416015625000000000000000,0.100001999840012789633192369365133345127105712890625000000000 magnesium,Mg,25.982593019999999484070940525270998477935791015625000000000000,0.110121190304775615209642580794024979695677757263183593750000 aluminium,Al,26.981538579999998717084963573142886161804199218750000000000000,1.000000000000000000000000000000000000000000000000000000000000 silicon,Si,27.976926535299998732853055116720497608184814453125000000000000,0.922220833349999713490774411184247583150863647460937500000000 silicon,Si,28.976494665299998843011053395457565784454345703125000000000000,0.046858437698747611166449900110819726251065731048583984375000 silicon,Si,29.973770011999999240970282698981463909149169921875000000000000,0.030920728951252581667707985957349592354148626327514648437500 phosphorus,P,30.973761998600000566739254281856119632720947265625000000000000,1.000000000000000000000000000000000000000000000000000000000000 sulfur,S,31.972071174100001655915548326447606086730957031250000000000000,0.949850011999040066967836537514813244342803955078125000000000 sulfur,S,32.971458910099997297038498800247907638549804687500000000000000,0.007519398448124149821059081233443066594190895557403564453125 sulfur,S,33.967867030000000738709786674007773399353027343750000000000000,0.042520598352131823427502155254842364229261875152587890625000 sulfur,S,35.967081200000002638716978253796696662902832031250000000000000,0.000109991200703943683199964587160479823069181293249130249023 chlorine,Cl,34.968852730000001827193045755848288536071777343750000000000000,0.757594848103037898923162174469325691461563110351562500000000 chlorine,Cl,36.965902640000003032128006452694535255432128906250000000000000,0.242405151896962045565686594272847287356853485107421875000000 argon,Ar,35.967545119999996927617758046835660934448242187500000000000000,0.003336205796380696270847510120916012965608388185501098632813 argon,Ar,37.962732199999997817485564155504107475280761718750000000000000,0.000629799206452999775149304007015871320618316531181335449219 argon,Ar,39.962383121999998536466591758653521537780761718750000000000000,0.996033994997166272078459314798237755894660949707031250000000 potassium,K,38.963706492999996555681718746200203895568847656250000000000000,0.932580526071084436878777523816097527742385864257812500000000 potassium,K,39.963998240000002226679498562589287757873535156250000000000000,0.000117099885242112454345267402722186034225160256028175354004 potassium,K,40.961825263000001484670065110549330711364746093750000000000000,0.067302374043673424131029037198459263890981674194335937500000 calcium,Ca,39.962590919999996685874066315591335296630859375000000000000000,0.969400838426726974006442105746828019618988037109375000000000 calcium,Ca,41.958618100000002471006155246868729591369628906250000000000000,0.006472228417153705684605746739634923869743943214416503906250 calcium,Ca,42.958766199999999457759258802980184555053710937500000000000000,0.001350985058105257227353823701321289263432845473289489746094 calcium,Ca,43.955482199999998726980265928432345390319824218750000000000000,0.020860869278785776348428271376178599894046783447265625000000 calcium,Ca,45.953691999999996653514244826510548591613769531250000000000000,0.000042999524425259849917842214228613784143817611038684844971 calcium,Ca,47.952522889999997346421878319233655929565429687500000000000000,0.001872079294802999303859447621789513505063951015472412109375 scandium,Sc,44.955908600000000774343789089471101760864257812500000000000000,1.000000000000000000000000000000000000000000000000000000000000 titanium,Ti,45.952628300000000649561116006225347518920898437500000000000000,0.082520097588289403889305617667559999972581863403320312500000 titanium,Ti,46.951759299999999086594471009448170661926269531250000000000000,0.074411070671519405350657905273692449554800987243652343750000 titanium,Ti,47.947942300000001125681592384353280067443847656250000000000000,0.737141543014838140912559083517407998442649841308593750000000 titanium,Ti,48.947866300000001160697138402611017227172851562500000000000000,0.054113506379234489751528514034362160600721836090087890625000 titanium,Ti,49.944787300000001550870365463197231292724609375000000000000000,0.051813782346118462951434224805780104361474514007568359375000 vanadium,V,49.947156700000000739692040951922535896301269531250000000000000,0.002503979968160254584302881752932989911641925573348999023438 vanadium,V,50.943957699999998567363945767283439636230468750000000000000000,0.997496020031839680797247638111002743244171142578125000000000 chromium,Cr,49.946042699999999570081854471936821937561035156250000000000000,0.043450743830478963380947732275672024115920066833496093750000 chromium,Cr,51.940506399999996745009411824867129325866699218750000000000000,0.837881075122238416774678171350387856364250183105468750000000 chromium,Cr,52.940648400000000606269168201833963394165039062500000000000000,0.095010483865806516501351097758742980659008026123046875000000 chromium,Cr,53.938879399999997588111000368371605873107910156250000000000000,0.023657697181476075587447382986283628270030021667480468750000 manganese,Mn,54.938044300000001385342329740524291992187500000000000000000000,1.000000000000000000000000000000000000000000000000000000000000 iron,Fe,53.939609300000000757790985517203807830810546875000000000000000,0.058452792721208068904559240763774141669273376464843750000000 iron,Fe,55.934936299999996833776094717904925346374511718750000000000000,0.917532497856775930422656983864726498723030090332031250000000 iron,Fe,56.935393300000001204352884087711572647094726562500000000000000,0.021190743592002535267138085828264593146741390228271484375000 iron,Fe,57.933274300000000778254616307094693183898925781250000000000000,0.002823965830013456732028309659199294401332736015319824218750 cobalt,Co,58.933194399999997870054357917979359626770019531250000000000000,1.000000000000000000000000000000000000000000000000000000000000 nickel,Ni,57.935342300000002069282345473766326904296875000000000000000000,0.680769095231327558970235713786678388714790344238281250000000 nickel,Ni,59.930786300000001176613295683637261390686035156250000000000000,0.262230419610671172669924544607056304812431335449218750000000 nickel,Ni,60.931056300000001613170752534642815589904785156250000000000000,0.011399083035777891892426083586542517878115177154541015625000 nickel,Ni,61.928345399999997766826709266752004623413085937500000000000000,0.036346250253448952882706635136855766177177429199218750000000 nickel,Ni,63.927967399999999997817212715744972229003906250000000000000000,0.009255151868774300419340228529563319170847535133361816406250 copper,Cu,62.929598400000003266541170887649059295654296875000000000000000,0.691494255172344751692037334578344598412513732910156250000000 copper,Cu,64.927790599999994469726516399532556533813476562500000000000000,0.308505744827655137285660202906001359224319458007812500000000 zinc,Zn,63.929142599999998708426574012264609336853027343750000000000000,0.491645713885820234700929631799226626753807067871093750000000 zinc,Zn,65.926034700000002430897438898682594299316406250000000000000000,0.277325508740183801492662496457342058420181274414062500000000 zinc,Zn,66.927128699999997252234607003629207611083984375000000000000000,0.040405292597461665848879164286699960939586162567138671875000 zinc,Zn,67.924845700000005876972863916307687759399414062500000000000000,0.184515103497573135227227680843498092144727706909179687500000 zinc,Zn,69.925321999999994204699760302901268005371093750000000000000000,0.006108381278961075126765489784474993939511477947235107421875 gallium,Ga,68.925574900000000866384652908891439437866210937500000000000000,0.601079797840404217446064194518839940428733825683593750000000 gallium,Ga,70.924703699999994910285749938338994979858398437500000000000000,0.398920202159595671531633342965506017208099365234375000000000 germanium,Ge,69.924249700000004281719157006591558456420898437500000000000000,0.205705812301332946478993335404084064066410064697265625000000 germanium,Ge,71.922075860000006741756806150078773498535156250000000000000000,0.274503726116209989527305879164487123489379882812500000000000 germanium,Ge,72.923459039999997344239091034978628158569335937500000000000000,0.077504017086240106770844704442424699664115905761718750000000 germanium,Ge,73.921177760999995598467648960649967193603515625000000000000000,0.364982406812098314485837136089685373008251190185546875000000 germanium,Ge,75.921402720000003228051355108618736267089843750000000000000000,0.077304037684118531714716482383664697408676147460937500000000 arsenic,As,74.921595699999997464146872516721487045288085937500000000000000,1.000000000000000000000000000000000000000000000000000000000000 selenium,Se,73.922475910000002841115929186344146728515625000000000000000000,0.008938426836876709608015190156038443092256784439086914062500 selenium,Se,75.919213720000001899279595818370580673217773437500000000000000,0.093712506598838590798905556766840163618326187133789062500000 selenium,Se,76.919914259999998762395989615470170974731445312500000000000000,0.076302570747548426055573145276866853237152099609375000000000 selenium,Se,77.917309200000005375841283239424228668212890625000000000000000,0.237686167234566703143627819372341036796569824218750000000000 selenium,Se,79.916522900000003915010893251746892929077148437500000000000000,0.496053694549759227605534306348999962210655212402343750000000 selenium,Se,81.916700099999999906685843598097562789916992187500000000000000,0.087306634032410290746639702774700708687305450439453125000000 bromine,Br,78.918338099999999712963472120463848114013671875000000000000000,0.506898896176611657438115798868238925933837890625000000000000 bromine,Br,80.916290099999997664781403727829456329345703125000000000000000,0.493101103823388231539581738616107031702995300292968750000000 krypton,Kr,77.920365599999996675251168198883533477783203125000000000000000,0.003552948126957346328819165037771199422422796487808227539063 krypton,Kr,79.916378600000001597436494193971157073974609375000000000000000,0.022860666234272977725971998097520554438233375549316406250000 krypton,Kr,81.913483700000000453655957244336605072021484375000000000000000,0.115931407401451927463575941601447993889451026916503906250000 krypton,Kr,82.914127199999995809776009991765022277832031250000000000000000,0.115000220996773441783922464765055337920784950256347656250000 krypton,Kr,83.911497733000004473069566302001476287841796875000000000000000,0.569863179997571966950431487930472940206527709960937500000000 krypton,Kr,85.910610633000004554560291580855846405029296875000000000000000,0.172791577242972227423933873069472610950469970703125000000000 rubidium,Rb,84.911789742999999930361809674650430679321289062500000000000000,0.721691132354705722207199869444593787193298339843750000000000 rubidium,Rb,86.909180535999993821860698517411947250366210937500000000000000,0.278308867645294166770497668039752170443534851074218750000000 strontium,Sr,83.913419899999993845085555221885442733764648437500000000000000,0.005609775608975640752429381308274969342164695262908935546875 strontium,Sr,85.909261900000004175126377958804368972778320312500000000000000,0.098606055757769678349333730693615507334470748901367187500000 strontium,Sr,86.908878900000004819048626814037561416625976562500000000000000,0.070007199712011511372189431767765199765563011169433593750000 strontium,Sr,87.905613900000005855872586835175752639770507812500000000000000,0.825776968921243081922511919401586055755615234375000000000000 yttrium,Y,88.905842000000006919435691088438034057617187500000000000000000,1.000000000000000000000000000000000000000000000000000000000000 zirconium,Zr,89.904702000000000339241523761302232742309570312500000000000000,0.514422711621750239352479638910153880715370178222656250000000 zirconium,Zr,90.905642000000000280124368146061897277832031250000000000000000,0.112234410554393593262290096390643157064914703369140625000000 zirconium,Zr,91.905032000000005609763320535421371459960937500000000000000000,0.171550886397901253266340404479706194251775741577148437500000 zirconium,Zr,93.906311999999999784449755679816007614135742187500000000000000,0.173788376250214926521664438041625544428825378417968750000000 zirconium,Zr,95.908271999999996637598087545484304428100585937500000000000000,0.028003615175739928616627238966430013533681631088256835937500 niobium,Nb,92.906372000000004618414095602929592132568359375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 molybdenum,Mo,91.906808600000005071706254966557025909423828125000000000000000,0.145308494342837241086741073559096548706293106079101562500000 molybdenum,Mo,93.905085299999996095721144229173660278320312500000000000000000,0.091496458524138415957516201615362660959362983703613281250000 molybdenum,Mo,94.905839299999996683254721574485301971435546875000000000000000,0.158387558641321063435114524509117472916841506958007812500000 molybdenum,Mo,95.904676300000005539914127439260482788085937500000000000000000,0.166690329831184980147185115129104815423488616943359375000000 molybdenum,Mo,96.906018299999999499050318263471126556396484375000000000000000,0.095999792030779435014764544575882609933614730834960937500000 molybdenum,Mo,97.905405299999998192106431815773248672485351562500000000000000,0.243900902666405350327494261364336125552654266357421875000000 molybdenum,Mo,99.907472799999993640085449442267417907714843750000000000000000,0.098216463963333416886669624545902479439973831176757812500000 ruthenium,Ru,95.907590299999995409052644390612840652465820312500000000000000,0.055402974808013198682044020415560225956141948699951171875000 ruthenium,Ru,97.905296000000006984009814914315938949584960937500000000000000,0.018726273471579152340993346115283202379941940307617187500000 ruthenium,Ru,98.905934799999997153463482391089200973510742187500000000000000,0.127588609866636532030881312493875157088041305541992187500000 ruthenium,Ru,99.904214800000005425317795015871524810791015625000000000000000,0.126054915071900669465421174209041055291891098022460937500000 ruthenium,Ru,100.905577899999997271152096800506114959716796875000000000000000,0.170586053375378299268305681835045106709003448486328125000000 ruthenium,Ru,101.904344899999998119710653554648160934448242187500000000000000,0.315451225206183960558803391904802992939949035644531250000000 ruthenium,Ru,103.905432000000004677531251218169927597045898437500000000000000,0.186189948200308125203505937861336860805749893188476562500000 rhodium,Rh,102.905501999999998474777385126799345016479492187500000000000000,1.000000000000000000000000000000000000000000000000000000000000 palladium,Pd,101.905602000000001794433046597987413406372070312500000000000000,0.010207550187954890497099569302008603699505329132080078125000 palladium,Pd,103.904031099999997422855813056230545043945312500000000000000000,0.111463248820283120088525663504697149619460105895996093750000 palladium,Pd,104.905080900000001520311343483626842498779296875000000000000000,0.223336399264176588275176982278935611248016357421875000000000 palladium,Pd,105.903480900000005249239620752632617950439453125000000000000000,0.273264416540030363744762098576757125556468963623046875000000 palladium,Pd,107.903892900000002441629476379603147506713867187500000000000000,0.264546508837878890929573572066146880388259887695312500000000 palladium,Pd,109.905172600000000215914042200893163681030273437500000000000000,0.117181876349676070137029171291942475363612174987792968750000 silver,Ag,106.905091999999996232872945256531238555908203125000000000000000,0.518389668985958174118877650471404194831848144531250000000000 silver,Ag,108.904755100000002698834578040987253189086914062500000000000000,0.481610331014041714858819887012941762804985046386718750000000 cadmium,Cd,105.906460899999999014653440099209547042846679687500000000000000,0.012567197514954164816458614950533956289291381835937500000000 cadmium,Cd,107.904183900000006701702659483999013900756835937500000000000000,0.008928009053980960965657409644791187020018696784973144531250 cadmium,Cd,109.903007400000007010021363385021686553955078125000000000000000,0.124890149496662231087817929164884844794869422912597656250000 cadmium,Cd,110.904183399999993753226590342819690704345703125000000000000000,0.127983459688489453753845737082883715629577636718750000000000 cadmium,Cd,111.902763399999997773193172179162502288818359375000000000000000,0.241267197414976458658131264201074372977018356323242187500000 cadmium,Cd,112.904408300000000053842086344957351684570312500000000000000000,0.122184752800125570604272695618419675156474113464355468750000 cadmium,Cd,113.903365300000004367575456853955984115600585937500000000000000,0.287277937020044504823346187549759633839130401611328125000000 cadmium,Cd,115.904763200000004985668056178838014602661132812500000000000000,0.074901297010766587636254598692175932228565216064453125000000 indium,In,112.904062699999997221311787143349647521972656250000000000000000,0.042954845418549769675564675708301365375518798828125000000000 indium,In,114.903878789000003735054633580148220062255859375000000000000000,0.957045154581450119302132861776044592261314392089843750000000 tin,Sn,111.904824399999995421239873394370079040527343750000000000000000,0.009707379007667929146641050408561568474397063255310058593750 tin,Sn,113.902783700000000521868059877306222915649414062500000000000000,0.006608215781738930282018795736576066701672971248626708984375 tin,Sn,114.903344709999998940475052222609519958496093750000000000000000,0.003409079548521898664348306340343697229400277137756347656250 tin,Sn,115.901743100000004460525815375149250030517578125000000000000000,0.145370749897527656857576516813423950225114822387695312500000 tin,Sn,116.902954300000004650428309105336666107177734375000000000000000,0.076859248003039171148742525474517606198787689208984375000000 tin,Sn,117.901607299999994893369148485362529754638671875000000000000000,0.242144620952342848330118840749491937458515167236328125000000 tin,Sn,118.903311599999994996323948726058006286621093750000000000000000,0.085916802463334898676272644024720648303627967834472656250000 tin,Sn,119.902202700000003687819116748869419097900390625000000000000000,0.325722055045137792728127124064485542476177215576171875000000 tin,Sn,121.903441999999998301973391789942979812622070312500000000000000,0.046317494276545329023875297025369945913553237915039062500000 tin,Sn,123.905277799999993249002727679908275604248046875000000000000000,0.057944355024143474885978122301821713335812091827392578125000 antimony,Sb,120.903812000000002058186510112136602401733398437500000000000000,0.572091349038115315472907695948379114270210266113281250000000 antimony,Sb,122.904212000000001125954440794885158538818359375000000000000000,0.427908650961884573504789841535966843366622924804687500000000 tellurium,Te,119.904061999999996146470948588103055953979492187500000000000000,0.000909764371027903685079651907585684966761618852615356445313 tellurium,Te,121.903041000000001758962753228843212127685546875000000000000000,0.025505394102927340937991829150632838718593120574951171875000 tellurium,Te,122.904270999999994273821357637643814086914062500000000000000000,0.008927687728878220055350745099076448241248726844787597656250 tellurium,Te,123.902821000000002982233127113431692123413085937500000000000000,0.047401722953754971134898710261040832847356796264648437500000 tellurium,Te,124.904431000000002427441359031945466995239257812500000000000000,0.070696689557404629455916733604681212455034255981445312500000 tellurium,Te,125.903311000000002195520210079848766326904296875000000000000000,0.188376210561464557668998054396070074290037155151367187500000 tellurium,Te,127.904461699999998813837009947746992111206054687500000000000000,0.317407791382032011817670991149498149752616882324218750000000 tellurium,Te,129.906222759000002042739652097225189208984375000000000000000000,0.340774739342510235573513455165084451436996459960937500000000 iodine,I,126.904472999999995863618096336722373962402343750000000000000000,1.000000000000000000000000000000000000000000000000000000000000 xenon,Xe,123.905891999999994368408806622028350830078125000000000000000000,0.000952296533640617525774685336870106766582466661930084228516 xenon,Xe,125.904302999999998746716300956904888153076171875000000000000000,0.000890196759683794711613680217254795934422872960567474365234 xenon,Xe,127.903531799999996110273059457540512084960937500000000000000000,0.019102830465697103606848017420816177036613225936889648437500 xenon,Xe,128.904780864000002793545718304812908172607421875000000000000000,0.264005869018636762923790683998959138989448547363281250000000 xenon,Xe,129.903509409999998069906723685562610626220703125000000000000000,0.040709981815666186621971434078659513033926486968994140625000 xenon,Xe,130.905084200000004557296051643788814544677734375000000000000000,0.212323527142361190289676642350968904793262481689453125000000 xenon,Xe,131.904155094000003600740456022322177886962890625000000000000000,0.269085350529324029977829013660084456205368041992187500000000 xenon,Xe,133.905395700000013903263607062399387359619140625000000000000000,0.104356830141138279266499466757522895932197570800781250000000 xenon,Xe,135.907214487999993934863596223294734954833984375000000000000000,0.088573117593851946605099101361702196300029754638671875000000 caesium,Cs,132.905451967000004742658347822725772857666015625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 barium,Ba,129.906321999999988747731549665331840515136718750000000000000000,0.001060985146207953045902061539607075246749445796012878417969 barium,Ba,131.905061799999998584098648279905319213867187500000000000000000,0.001010985846198153050023993415607037604786455631256103515625 barium,Ba,133.904508200000009310315363109111785888671875000000000000000000,0.024171461599537605313692267827718751505017280578613281250000 barium,Ba,134.905688199999985954491421580314636230468750000000000000000000,0.065920277116120362670415033790050074458122253417968750000000 barium,Ba,135.904576200000008157076081261038780212402343750000000000000000,0.078541300421794094099858796198532218113541603088378906250000 barium,Ba,136.905827200000004495450411923229694366455078125000000000000000,0.112320827508414877726750091824214905500411987304687500000000 barium,Ba,137.905247199999990925789461471140384674072265625000000000000000,0.716974162361726841119491382414707913994789123535156250000000 lanthanum,La,137.907123000000012780219549313187599182128906250000000000000000,0.000888171872103250392010975744483403104823082685470581054688 lanthanum,La,138.906362000000001444277586415410041809082031250000000000000000,0.999111828127896672846475212281802669167518615722656250000000 cerium,Ce,135.907129300000008242932381108403205871582031250000000000000000,0.001851973331584025024912354417949700291501358151435852050781 cerium,Ce,137.905998000000010961230145767331123352050781250000000000000000,0.002511963827720880421123794690174690913408994674682617187500 cerium,Ce,139.905441999999993640813045203685760498046875000000000000000000,0.884492463308528265031327464384958148002624511718750000000000 cerium,Ce,141.909252000000009275026968680322170257568359375000000000000000,0.111143599532166723053983048430382041260600090026855468750000 praseodymium,Pr,140.907661999999987756382324732840061187744140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 neodymium,Nd,141.907732000000009975337889045476913452148437500000000000000000,0.271519166958828106483991859931848011910915374755859375000000 neodymium,Nd,142.909821999999991248841979540884494781494140625000000000000000,0.121740433020292235233306143982190405949950218200683593750000 neodymium,Nd,143.910091999999991685399436391890048980712890625000000000000000,0.237977663997580829446931716120161581784486770629882812500000 neodymium,Nd,144.912581999999986237526172772049903869628906250000000000000000,0.082929723850915446070608538775559281930327415466308593750000 neodymium,Nd,145.913121999999987110641086474061012268066406250000000000000000,0.171890140355501652713599014532519504427909851074218750000000 neodymium,Nd,147.916901999999993222445482388138771057128906250000000000000000,0.057561075412857647115583148433870519511401653289794921875000 neodymium,Nd,149.920902000000012321834219619631767272949218750000000000000000,0.056381796404024006608146635244338540360331535339355468750000 samarium,Sm,143.912012000000004263711161911487579345703125000000000000000000,0.030772522277086666181444840617587033193558454513549804687500 samarium,Sm,146.914902000000012094460544176399707794189453125000000000000000,0.149881578776357327065227309503825381398200988769531250000000 samarium,Sm,147.914831999999989875504979863762855529785156250000000000000000,0.112382691006085513873991033051424892619252204895019531250000 samarium,Sm,148.917192000000000007275957614183425903320312500000000000000000,0.138246406123312015612469849656918086111545562744140625000000 samarium,Sm,149.917282000000000152795109897851943969726562500000000000000000,0.073792068527347848272412988990254234522581100463867187500000 samarium,Sm,151.919742000000013604221749119460582733154296875000000000000000,0.267451009404714612482933944193064235150814056396484375000000 samarium,Sm,153.922222000000004982211976312100887298583984375000000000000000,0.227473723885095902019770619517657905817031860351562500000000 europium,Eu,150.919861999999994850440998561680316925048828125000000000000000,0.478103065570820051632949798658955842256546020507812500000000 europium,Eu,152.921242000000006555637810379266738891601562500000000000000000,0.521896934429179837344747738825390115380287170410156250000000 gadolinium,Gd,151.919802000000004227331373840570449829101562500000000000000000,0.002009636255837693018938550082452820788603276014328002929688 gadolinium,Gd,153.920872000000002799424692057073116302490234375000000000000000,0.021826049485043207132317633067941642366349697113037109375000 gadolinium,Gd,154.922631999999993013261700980365276336669921875000000000000000,0.147985214676143617129611129712429828941822052001953125000000 gadolinium,Gd,155.922132000000004836692824028432369232177734375000000000000000,0.204672954195290635048820604424690827727317810058593750000000 gadolinium,Gd,156.923971999999992021912476047873497009277343750000000000000000,0.156491675006823760529783839956508018076419830322265625000000 gadolinium,Gd,157.924112000000008038114174269139766693115234375000000000000000,0.248435033258980114689862261911912355571985244750976562500000 gadolinium,Gd,159.927062000000006491973181255161762237548828125000000000000000,0.218579437121880937322515592313720844686031341552734375000000 terbium,Tb,158.925352000000003727109287865459918975830078125000000000000000,1.000000000000000000000000000000000000000000000000000000000000 dysprosium,Dy,155.924282000000005155015969648957252502441406250000000000000000,0.000562985756460361477619691594753703611786477267742156982422 dysprosium,Dy,157.924421999999992749508237466216087341308593750000000000000000,0.000952975889709990254573812595850768047966994345188140869141 dysprosium,Dy,159.925202000000012958480510860681533813476562500000000000000000,0.023291210732368467645203580218549177516251802444458007812500 dysprosium,Dy,160.926941999999996824044501408934593200683593750000000000000000,0.188889421097646226233024435714469291269779205322265625000000 dysprosium,Dy,161.926812000000012403688742779195308685302734375000000000000000,0.254747154896981076177553404704667627811431884765625000000000 dysprosium,Dy,162.928741999999999734427547082304954528808593750000000000000000,0.248957901365095435330943018925609067082405090332031250000000 dysprosium,Dy,163.929181999999997287886799313127994537353515625000000000000000,0.282598350261738351374418698469526134431362152099609375000000 holmium,Ho,164.930331999999992831362760625779628753662109375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 erbium,Er,161.928791999999987183400662615895271301269531250000000000000000,0.001395973476503946332158423437874716910300776362419128417969 erbium,Er,163.929212000000006810296326875686645507812500000000000000000000,0.016012695758780580435054474719436257146298885345458984375000 erbium,Er,165.930302000000011730662663467228412628173828125000000000000000,0.335027234482544788995994622382568195462226867675781250000000 erbium,Er,166.932051999999998770363163203001022338867187500000000000000000,0.228686654953555862368475004586798604577779769897460937500000 erbium,Er,167.932381999999989830030244775116443634033203125000000000000000,0.269776674243189351631855288360384292900562286376953125000000 erbium,Er,169.935472000000004300090949982404708862304687500000000000000000,0.149100767085425356395234075534972362220287322998046875000000 thulium,Tm,168.934222000000005436959327198565006256103515625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 ytterbium,Yb,167.933891999999985955582815222442150115966796875000000000000000,0.001232929969577727796758992440118163358420133590698242187500 ytterbium,Yb,169.934772000000009484210750088095664978027343750000000000000000,0.029822206098693591902470956256365752778947353363037109375000 ytterbium,Yb,170.936331999999993058736436069011688232421875000000000000000000,0.140905996539396560773838018576498143374919891357421875000000 ytterbium,Yb,171.936392000000012103555491194128990173339843750000000000000000,0.216800685721051017429417129278590437024831771850585937500000 ytterbium,Yb,172.938221999999996114638634026050567626953125000000000000000000,0.161027253651992552363481081556528806686401367187500000000000 ytterbium,Yb,173.938872000000003481545718386769294738769531250000000000000000,0.320249909805123023076589561242144554853439331054687500000000 ytterbium,Yb,175.942581999999987374394549988210201263427734375000000000000000,0.129961018214165419104588750087714288383722305297851562500000 lutetium,Lu,174.940782000000012885720934718847274780273437500000000000000000,0.974008767577204226384424146090168505907058715820312500000000 lutetium,Lu,175.942691999999993868186720646917819976806640625000000000000000,0.025991232422795697287742910930319339968264102935791015625000 hafnium,Hf,173.940052000000008547431207261979579925537109375000000000000000,0.001609652315099938373749166586890169128309935331344604492188 hafnium,Hf,175.941412000000013904355000704526901245117187500000000000000000,0.052668623577307296934613134453684324398636817932128906250000 hafnium,Hf,176.943231999999994741301634348928928375244140625000000000000000,0.185969830516608397585898160286888014525175094604492187500000 hafnium,Hf,177.943712000000004991306923329830169677734375000000000000000000,0.272821070648739838482299546740250661969184875488281250000000 hafnium,Hf,178.945821999999992613084032200276851654052734375000000000000000,0.136190582834107815068946933934057597070932388305664062500000 hafnium,Hf,179.946562000000000125510268844664096832275390625000000000000000,0.350740240108136591690168870627530850470066070556640625000000 tantalum,Ta,179.947462000000001580701791681349277496337890625000000000000000,0.000120131992311552486551486096377772128107608295977115631104 tantalum,Ta,180.948002000000002453816705383360385894775390625000000000000000,0.999879868007688354936135510797612369060516357421875000000000 tungsten,W,179.946711999999990894139045849442481994628906250000000000000000,0.001209872963338849303702171589236513682408258318901062011719 tungsten,W,181.948204699999990907599567435681819915771484375000000000000000,0.264988176241494621798722164385253563523292541503906250000000 tungsten,W,182.950223700000009330324246548116207122802734375000000000000000,0.143124971877952811283307710255030542612075805664062500000000 tungsten,W,183.950931700000012369855539873242378234863281250000000000000000,0.306387829277925793913794905165559612214565277099609375000000 tungsten,W,185.954362000000003263266989961266517639160156250000000000000000,0.284289149639287863635672692907974123954772949218750000000000 rhenium,Re,184.952955900000006295158527791500091552734375000000000000000000,0.374005039798408045470523575204424560070037841796875000000000 rhenium,Re,186.955750999999992245648172684013843536376953125000000000000000,0.625994960201591843507173962279921397566795349121093750000000 osmium,Os,183.952489100000008193092071451246738433837890625000000000000000,0.000209947723016968765524098428087995671376120299100875854492 osmium,Os,185.953841000000011263182386755943298339843750000000000000000000,0.015926034417430057904541129687459033448249101638793945312500 osmium,Os,186.955750999999992245648172684013843536376953125000000000000000,0.019615115836156795520173190539026109036058187484741210937500 osmium,Os,187.955840999999992391167324967682361602783203125000000000000000,0.132457018202467580181291850749403238296508789062500000000000 osmium,Os,188.958142000000009375071385875344276428222656250000000000000000,0.161519781574387955025429164379602298140525817871093750000000 osmium,Os,189.958441999999990912328939884901046752929687500000000000000000,0.262554623898649197588639481182326562702655792236328125000000 osmium,Os,191.961481999999989511707099154591560363769531250000000000000000,0.407717478347891348899878494194126687943935394287109375000000 iridium,Ir,190.960591999999991230652085505425930023193359375000000000000000,0.373050779688124722888176165724871680140495300292968750000000 iridium,Ir,192.962921999999991840013535693287849426269531250000000000000000,0.626949220311875166089521371759474277496337890625000000000000 platinum,Pt,189.959934000000004061803338117897510528564453125000000000000000,0.000121987349911814132899338936066868654961581341922283172607 platinum,Pt,191.961041999999991958247846923768520355224609375000000000000000,0.007821588901230941415221309398475568741559982299804687500000 platinum,Pt,193.962681699999990314609021879732608795166015625000000000000000,0.328605923565726210089366077227168716490268707275390625000000 platinum,Pt,194.964792700000003833338269032537937164306640625000000000000000,0.337788971283677852408544595164130441844463348388671875000000 platinum,Pt,195.964952699999997776103555224835872650146484375000000000000000,0.252107856415289710572125159160350449383258819580078125000000 platinum,Pt,197.967892000000006191839929670095443725585937500000000000000000,0.073553672484163390432598816914833150804042816162109375000000 gold,Au,196.966569600000013906537787988781929016113281250000000000000000,1.000000000000000000000000000000000000000000000000000000000000 mercury,Hg,195.965832000000006019035936333239078521728515625000000000000000,0.001509815802472098391837085351596670079743489623069763183594 mercury,Hg,197.966769300000009934592526406049728393554687500000000000000000,0.099707835644051417967048678292485419660806655883789062500000 mercury,Hg,198.968281300000001010630512610077857971191406250000000000000000,0.168701418426951910145561441822792403399944305419921875000000 mercury,Hg,199.968327299999998558632796630263328552246093750000000000000000,0.230990819120067331082779560347262304276227951049804687500000 mercury,Hg,200.970303599999994048630469478666782379150390625000000000000000,0.131793921141620695713925215386552736163139343261718750000000 mercury,Hg,201.970643599999988282434060238301753997802734375000000000000000,0.298589572072207154462830658303573727607727050781250000000000 mercury,Hg,203.973494299999998702332959510385990142822265625000000000000000,0.068706617792629293139938795320631470531225204467773437500000 thallium,Tl,202.972345100000012507734936662018299102783203125000000000000000,0.295204095918081610427918803907232359051704406738281250000000 thallium,Tl,204.974428100000011454540072008967399597167968750000000000000000,0.704795904081918278549778733577113598585128784179687500000000 lead,Pb,203.973044899999990775540936738252639770507812500000000000000000,0.014094362255097959285565778486670751590281724929809570312500 lead,Pb,205.974466900000010127769201062619686126708984375000000000000000,0.241003598560575765796798464180028531700372695922851562500000 lead,Pb,206.975897900000006757181836292147636413574218750000000000000000,0.221011595361855245345239495691203046590089797973632812500000 lead,Pb,207.976653900000002295200829394161701202392578125000000000000000,0.523890443822470963652904174523428082466125488281250000000000 bismuth,Bi,208.980401000000000522049958817660808563232421875000000000000000,1.000000000000000000000000000000000000000000000000000000000000 uranium,U,234.040952000000004318280844017863273620605468750000000000000000,0.000054599923560107009460132254652364736102754250168800354004 uranium,U,235.043932000000012294549378566443920135498046875000000000000000,0.007204689913434121108226637630878030904568731784820556640625 uranium,U,238.050792000000001280568540096282958984375000000000000000000000,0.992740710163005690702675565262325108051300048828125000000000 thorium,Th,232.038061999999996487531461752951145172119140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 protactinium,Pa,231.035881999999986646798788569867610931396484375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/leucine.svg000664 001750 001750 00000004125 15100504560 035007 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/lysine.svg000664 001750 001750 00000004454 15100504560 034673 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/methionine.svg000664 001750 001750 00000004340 15100504560 035442 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/modification_dictionary000664 001750 001750 00000003066 15100504560 037403 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the modification_dictionary file where the correspondences # between the name of each modification and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. Also, the graphical operation that is to # be performed upon chemical modification of a monomer is listed ('T' # for transparent and 'O' for opaque). See the manual for details. # The format of the file is like this : # ------------------------------------- # Phosphorylation%T%phospho.svg # where Phosphorylation is the name of the modification. T indicates # that the visual rendering of the modification is a transparent # process (O indicates that the visual rendering of the modification # is a full image replacement 'O' like opaque). phospho.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Phosphorylation%T%phospho.svg Sulphation%T%sulpho.svg AmidationAsp%O%asparagine.svg Acetylation%T%acetyl.svg AmidationGlu%O%glutamine.svg Oxidation%T%oxidation.svg SulfideBond%T%sulfbond.svg ProtonLoss%T%protonloss.svg tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/monomer_dictionary000664 001750 001750 00000002426 15100504560 036411 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the monomer_dictionary file where the correspondences # between the codes of each monomer and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. # The format of the file is like this : # ------------------------------------- # Ala%alanine.svg # where Ala is the monomer code and alanine.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Ala%alanine.svg Cys%cysteine.svg Asp%aspartate.svg Glu%glutamate.svg Phe%phenylalanine.svg Gly%glycine.svg His%histidine.svg Ile%isoleucine.svg Lys%lysine.svg Leu%leucine.svg Met%methionine.svg Asn%asparagine.svg Pro%proline.svg Gln%glutamine.svg Arg%arginine.svg Ser%serine.svg Thr%threonine.svg Val%valine.svg Trp%tryptophan.svg Tyr%tyrosine.svg tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/oxidation.svg000664 001750 001750 00000005727 15100504560 035313 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/pdb-code-conversion.dic000664 001750 001750 00000000557 15100504560 037111 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# Converts from code on the left of '>' to code on the right. # Number of letters allowed in each code is # described with syntax 1>3 and that line should be the first # non-comment line in this file. 3>3 ALA>Ala CYS>Cys ASP>Asp GLU>Glu PHE>Phe GLY>Gly HIS>His ILE>Ile LYS>Lys LEU>Leu MET>Met ASN>Asn PRO>Pro GLN>Gln ARG>Arg SER>Ser THR>Thr VAL>Val TRP>Trp TYR>Tyr tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/phenylalanine.svg000664 001750 001750 00000004672 15100504560 036142 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/phospho.svg000664 001750 001750 00000002365 15100504560 035047 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/proline.svg000664 001750 001750 00000004064 15100504560 035035 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/protein-3-letters.xml000664 001750 001750 00000023246 15100504560 036612 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> protein-3-letters "N-term amine" +H "C-term carboxylic acid" +OH 3 +H 1 1 Glycine Gly C2H3NO Alanine Ala C3H5NO Valine Val C5H9NO Leucine Leu C6H11NO DiaminoPimelic Dap C7H12N2O3 Isoleucine Ile C6H11NO Serine Ser C3H5NO2 Threonine Thr C4H7NO2 Cysteine Cys C3H5NOS Methionine Met C5H9NOS Arginine Arg C6H12N4O Lysine Lys C6H12N2O Aspartate Asp C4H5NO3 Glutamate Glu C5H7NO3 Asparagine Asn C4H6N2O2 Glutamine Gln C5H8N2O2 Tryptophan Trp C11H10N2O Phenylalanine Phe C9H9N1O Tyrosine Tyr C9H9N1O2 Histidine His C6H7N3O Proline Pro C5H7N1O1 Acetylation C2H2O1 ;Lys; 1 AmidationAsp H1N1-O1 ;Asp; 1 AmidationGlu H1N1-O1 ;Glu; 1 Carbamidomethylation C2H3N1O1 ;Cys; 1 Carbamylation C1H1N1O1 ;Lys; 1 CarboxyMethylation C2H2O2 ;Cys; 1 Chromo-O -O1 ;Thr;Ser;Gly; 1 Chromo-H -H1 ;Gly; 1 Chromo-H3 -H3 ;Tyr;Phe;Trp; 1 Dehydroalanine -H2S1 ;Cys; 1 DTNB (adsorbed) C14H8N2O8S2 ;Cys; 1 Dehydroxylation -H1O1 ;Asp;Glu; 1 Formylation C1O1 * 1 GlcNAcMurNAc(R) C19H32N2O12 * 1 Glutamylation C5H7N1O3 ;Glu; 6 Glycylation C2H3N1O1 ;Glu; 34 Hydroxylation H1O1 ;Phe; 1 LysMethylation C1H2 ;Lys; 3 Methylation C1H2 ;Lys;Arg; 2 Oxidation O1 ;Met;Tyr; 1 Phosphorylation H1O3P1 ;Ser;Thr;Tyr; 1 ProtonLoss -H1 ;Cys;Tyr;Lys; 1 SulfideBond -H2 ;Cys; 1 Sulphation O3S1 ;Ser;Thr;Tyr; 1 SPITC C7H5N1O3S2 * 1 TNB C7H3N1O4S1 ;Cys; 1 CFP-chromophore Chromo-O Chromo-H3 Chromo-H DisulfideBond ProtonLoss ProtonLoss EndoAspN+GluN /Asp;/Glu Trypsin Lys/;Arg/;-Lys/Pro Chymotrypsin Trp/;Val/ EndoLysC Lys/ EndoAspN /Asp GluC Glu/ CyanogenBromide Met/ Homoseryl Met -CH2S+O EndoAspN-Trypsin /Asp;Lys/;Arg/;-Lys/Pro a LE -C1O1H1 0 b LE -H 0 c LE +N1H2 0 that's just a comment z RE -N1H1 0 Typically in ECD of protonated ions y RE +H1 0 x RE +C1O1-H1 0 imm NE -C1O1 0 tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/protonloss.svg000664 001750 001750 00000004266 15100504560 035534 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/serine.svg000664 001750 001750 00000005230 15100504560 034646 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/sulfbond.svg000664 001750 001750 00000011620 15100504560 035175 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/sulpho.svg000664 001750 001750 00000006334 15100504560 034701 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/threonine.svg000664 001750 001750 00000003560 15100504560 035301 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/tryptophan.svg000664 001750 001750 00000003744 15100504560 035522 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/tyrosine.svg000664 001750 001750 00000003520 15100504560 035235 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-1/protein-3-letters/valine.svg000664 001750 001750 00000004147 15100504560 034645 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/000775 001750 001750 00000000000 15100507137 027344 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/000775 001750 001750 00000000000 15100507137 032457 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/acetyl.svg000664 001750 001750 00000006234 15100504560 034463 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/alanine.svg000664 001750 001750 00000003747 15100504560 034617 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/arginine.svg000664 001750 001750 00000005364 15100504560 035001 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/asparagine.svg000664 001750 001750 00000003672 15100504560 035240 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/aspartate.svg000664 001750 001750 00000004360 15100504560 035164 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/cfp-chromophore.svg000664 001750 001750 00000017430 15100504560 036216 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml T G F libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/chemPad.conf000664 001750 001750 00000014633 15100504560 034673 0ustar00rusconirusconi000000 000000 # This is the layout for the chempad of the molecular calculator 'extremecalc' # in the massXpert software program. # Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Filippo Rusconi. # GNU General Public License. # The format of this file allows the definition of colors # according to the following syntax: # # color%OneColor%xxx,yyy,zzz # # with xxx, yyy and zzz numerical values in range [0,255] for the red, # green and blue component of the color respectively. # # Available colors are: white, black, red, darkRed, green, darkGreen, # blue, darkBlue, cyan, darkCyan, magenta, darkMagenta, yellow, # darkYellow, gray, darkGray, lightGray, please, see # http://www.w3.org/TR/SVG/types.html#ColorKeywords for a list of # commonly used colors associated to their rgb value. # # A separator might be used between sections of buttons. Its syntax is: # Note the double && that acts as an escape of the &. # # chempadsep%Hexoses && Fucose%[midnightblue] # # The color specification (%[midnightblue]) is not compulsory # General button syntax is like the example below: # # chempadkey%hydrate%+H2O1%adds a water molecule%[red,blue] # # Each line is divided into 4 compulsory elements: # 'chempadkey' starts the line # 'hydrate' is the label of the button # '+H2O1' is the formula that is applied when the button gets clicked # 'adds a water molecule' is the text to display in the tooltip # '[red,blue]' are the background and foreground colors, respectively # # The colors specification (last %[color,color]) is not compulsory # # The geometry is specified by the number of columns of buttons: # chempad_columns%3 would specify that all the buttons in the # chemical pad should be laid out using three columns and as # many rows as necessary to display them all. chempad_columns%3 color%aliceblue%240,248,255 color%antiquewhite%250,235,215 color%aqua%0,255,255 color%aquamarine%127,255,212 color%azure%240,255,255 color%beige%245,245,220 color%bisque%255,228,196 color%black%0,0,0 color%blanchedalmond%255,235,205 color%blue%0,0,255 color%blueviolet%138,43,226 color%brown%165,42,42 color%burlywood%222,184,135 color%cadetblue%95,158,160 color%chartreuse%127,255,0 color%chocolate%210,105,30 color%coral%255,127,80 color%cornflowerblue%100,149,237 color%cornsilk%255,248,220 color%crimson%220,20,60 color%cyan%0,255,255 color%darkblue%0,0,139 color%darkcyan%0,139,139 color%darkgoldenrod%184,134,11 color%darkgray%169,169,169 color%darkgreen%0,100,0 color%darkgrey%169,169,169 color%darkkhaki%189,183,107 color%darkmagenta%139,0,139 color%darkolivegreen%85,107,47 color%darkorange%255,140,0 color%darkorchid%153,50,204 color%darkred%139,0,0 color%darksalmon%233,150,122 color%darkseagreen%143,188,143 color%darkslateblue%72,61,139 color%darkslategray%47,79,79 color%darkslategrey%47,79,79 color%darkturquoise%0,206,209 color%darkviolet%148,0,211 color%deeppink%255,20,147 color%deepskyblue%0,191,255 color%dimgray%105,105,105 color%dimgrey%105,105,105 color%dodgerblue%30,144,255 color%firebrick%178,34,34 color%floralwhite%255,250,240 color%forestgreen%34,139,34 color%fuchsia%255,0,255 color%gainsboro%220,220,220 color%ghostwhite%248,248,255 color%gold%255,215,0 color%goldenrod%218,165,32 color%gray%128,128,128 color%grey%128,128,128 color%green%0,128,0 color%greenyellow%173,255,47 color%honeydew%240,255,240 color%hotpink%255,105,180 color%indianred%205,92,92 color%indigo%75,0,130 color%ivory%255,255,240 color%khaki%240,230,140 color%lavender%230,230,250 color%lavenderblush%255,240,245 color%lawngreen%124,252,0 color%lemonchiffon%255,250,205 color%lightblue%173,216,230 color%lightcoral%240,128,128 color%lightcyan%224,255,255 color%lightgoldenrodyellow%250,250,210 color%lightgray%211,211,211 color%lightgreen%144,238,144 color%lightgrey%211,211,211 color%lightpink%255,182,193 color%lightsalmon%255,160,122 color%lightseagreen%32,178,170 color%lightskyblue%135,206,250 color%lightslategray%119,136,153 color%lightslategrey%119,136,153 color%lightsteelblue%176,196,222 color%lightyellow%255,255,224 color%lime%0,255,0 color%limegreen%50,205,50 color%linen%250,240,230 color%magenta%255,0,255 color%maroon%128,0,0 color%mediumaquamarine%102,205,170 color%mediumblue%0,0,205 color%mediumorchid%186,85,211 color%mediumpurple%147,112,219 color%mediumseagreen%60,179,113 color%mediumslateblue%123,104,238 color%mediumspringgreen%0,250,154 color%mediumturquoise%72,209,204 color%mediumvioletred%199,21,133 color%midnightblue%25,25,112 color%mintcream%245,255,250 color%mistyrose%255,228,225 color%moccasin%255,228,181 color%navajowhite%255,222,173 color%navy%0,0,128 color%oldlace%253,245,230 color%olive%128,128,0 color%olivedrab%107,142,35 color%orange%255,165,0 color%orangered%255,69,0 color%orchid%218,112,214 color%palegoldenrod%238,232,170 color%palegreen%152,251,152 color%paleturquoise%175,238,238 color%palevioletred%219,112,147 color%papayawhip%255,239,213 color%peachpuff%255,218,185 color%peru%205,133,63 color%pink%255,192,203 color%plum%221,160,221 color%powderblue%176,224,230 color%purple%128,0,128 color%red%255,0,0 color%rosybrown%188,143,143 color%royalblue%65,105,225 color%saddlebrown%139,69,19 color%salmon%250,128,114 color%sandybrown%244,164,96 color%seagreen%46,139,87 color%seashell%255,245,238 color%sienna%160,82,45 color%silver%192,192,192 color%skyblue%135,206,235 color%slateblue%106,90,205 color%slategray%112,128,144 color%slategrey%112,128,144 color%snow%255,250,250 color%springgreen%0,255,127 color%steelblue%70,130,180 color%tan%210,180,140 color%teal%0,128,128 color%thistle%216,191,216 color%tomato%255,99,71 color%turquoise%64,224,208 color%violet%238,130,238 color%wheat%245,222,179 color%white%255,255,255 color%whitesmoke%245,245,245 color%yellow%255,255,0 color%yellowgreen%154,205,50 chempad_columns%3 chempadgroup%Generic chempadkey%protonate%+H1%adds a proton chempadkey%hydrate%+H2O1%adds a water molecule chempadkey%0H-ylate%+O1H1%adds an hydroxyl group chempadkey%acetylate%-H1+C2H3O1%adds an acetyl group chempadkey%phosphorylate%-H+H2PO3%add a phosphate group chempadkey%sulfide bond%-H2%oxydizes with loss of hydrogen chempadgroup%Hexoses%[seagreen] chempadkey%Res-GlucNAc%+C8H14N1O6%residue GlcNAc%[lawngreen,black] chempadkey%Res-Lactoyl-MurNAc%+C11H17N1O6%residue MurNAc+Lactoyl%[lawngreen,black] chempadgroup%Step residues%[tomato] chempadkey%Res-mDAP%+C7H12N2O3%residue mDAP%[tomato,black] chempadkey%Res-iGlu%+C5H7N1O3%residue iGlu%[tomato,black] chempadkey%Res-Ala%+C3H5N1O1%residue Ala%[tomato,black] chempadkey%Res-iLys%+C6H12N2O2%residue iLys%[tomato,black] tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/cross_linker_dictionary000664 001750 001750 00000002204 15100504560 037240 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007,2008 Filippo Rusconi # This is the cross_linker_dictionary file where the correspondences # between the name of each cross-link and their graphic file (svg file # called "image") used to graphicallly render them in the sequence # editor are made. See the manual for details. # The format of the file is like this : # ------------------------------------- # DisulfideBond%disulfidebond.svg # where DisulfideBond is the name of the cross-link. disulfidebond.svg # is a resolution-independent svg file to be used to render the # cross-link vignette. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. DisulfideBond%disulfidebond-cross-link.svg CFP-chromophore%cfp-chromophore.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/cursor.svg000664 001750 001750 00000002144 15100504560 034513 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/cysteine.svg000664 001750 001750 00000004561 15100504560 035026 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/default-cross-link-vignette.svg000664 001750 001750 00000013740 15100504560 040454 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/default-modif-vignette.svg000664 001750 001750 00000017145 15100504560 037471 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/diaminopimelic.svg000664 001750 001750 00000006601 15100504560 036104 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml A P tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/disulfidebond-cross-link.svg000664 001750 001750 00000015615 15100504560 040023 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml S S libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/glutamate.svg000664 001750 001750 00000003751 15100504560 035166 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/glutamine.svg000664 001750 001750 00000005231 15100504560 035163 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/glutamylation.svg000664 001750 001750 00000003561 15100504560 036014 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml E libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/glycine.svg000664 001750 001750 00000004733 15100504560 034636 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/glycylation.svg000664 001750 001750 00000003605 15100504560 035460 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml G libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/histidine.svg000664 001750 001750 00000003751 15100504560 035163 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/hydroxylation.svg000664 001750 001750 00000005605 15100504560 036041 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/isoleucine.svg000664 001750 001750 00000003137 15100504560 035261 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/isotopic-data.dat000664 001750 001750 00000116230 15100504560 035632 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file contains isotopic data in a format that can accommodate # comments in the form of lines beginning with the '#' character. hydrogen,H,1.007825032269999976364260874106548726558685302734375000000000,0.999884290164307909520857720053754746913909912109375000000000 hydrogen,H,2.014101778190000135992931973305530846118927001953125000000000,0.000115709835692033314582735648023970043141162022948265075684 helium,He,3.016029322000000068015879151062108576297760009765625000000000,0.000001342999991941999914655050951672876635711872950196266174 helium,He,4.002603254140000288430201180744916200637817382812500000000000,0.999998657000008006612290500925155356526374816894531250000000 lithium,Li,6.015122887100000426130463893059641122817993164062500000000000,0.075933925285977116326208147256693337112665176391601562500000 lithium,Li,7.016003442999999784035480843158438801765441894531250000000000,0.924066074714022800407065005856566131114959716796875000000000 beryllium,Be,9.012183159999999304545781342312693595886230468750000000000000,1.000000000000000000000000000000000000000000000000000000000000 boron,B,10.012937300000000817590262158773839473724365234375000000000000,0.199480830670926506664741850727295968681573867797851562500000 boron,B,11.009305299999999405713424494024366140365600585937500000000000,0.800519169329073410068531302385963499546051025390625000000000 carbon,C,12.000000000000000000000000000000000000000000000000000000000000,0.989211941850466902614869013632414862513542175292968750000000 carbon,C,13.003354835199999683936766814440488815307617187500000000000000,0.010788058149533083507343178553128382191061973571777343750000 nitrogen,N,14.003074004200000146624915942084044218063354492187500000000000,0.996358014567941707717579902237048372626304626464843750000000 nitrogen,N,15.000108899400000694868140271864831447601318359375000000000000,0.003641985432058271465738386041266494430601596832275390625000 oxygen,O,15.994914620199999433225457323715090751647949218750000000000000,0.997567609729561044495937949250219389796257019042968750000000 oxygen,O,16.999131757600000725005884305573999881744384765625000000000000,0.000380998476006095935803702490218825005285907536745071411133 oxygen,O,17.999159613700001614233769942075014114379882812500000000000000,0.002051391794432822109073288885383590240962803363800048828125 fluorine,F,18.998403163700000817470936453901231288909912109375000000000000,1.000000000000000000000000000000000000000000000000000000000000 neon,Ne,19.992440181999999282425051205791532993316650390625000000000000,0.904766666333356561757739200402284041047096252441406250000000 neon,Ne,20.993846730000001343796611763536930084228515625000000000000000,0.002709810313278070148523823945652111433446407318115234375000 neon,Ne,21.991385120000000341633494826965034008026123046875000000000000,0.092523523353365264010328417043638182803988456726074218750000 sodium,Na,22.989769282000001027199687086977064609527587890625000000000000,1.000000000000000000000000000000000000000000000000000000000000 magnesium,Mg,23.985041701000000102794729173183441162109375000000000000000000,0.789876809855211581279377242026384919881820678710937500000000 magnesium,Mg,24.985837029999999003848643042147159576416015625000000000000000,0.100001999840012789633192369365133345127105712890625000000000 magnesium,Mg,25.982593019999999484070940525270998477935791015625000000000000,0.110121190304775615209642580794024979695677757263183593750000 aluminium,Al,26.981538579999998717084963573142886161804199218750000000000000,1.000000000000000000000000000000000000000000000000000000000000 silicon,Si,27.976926535299998732853055116720497608184814453125000000000000,0.922220833349999713490774411184247583150863647460937500000000 silicon,Si,28.976494665299998843011053395457565784454345703125000000000000,0.046858437698747611166449900110819726251065731048583984375000 silicon,Si,29.973770011999999240970282698981463909149169921875000000000000,0.030920728951252581667707985957349592354148626327514648437500 phosphorus,P,30.973761998600000566739254281856119632720947265625000000000000,1.000000000000000000000000000000000000000000000000000000000000 sulfur,S,31.972071174100001655915548326447606086730957031250000000000000,0.949850011999040066967836537514813244342803955078125000000000 sulfur,S,32.971458910099997297038498800247907638549804687500000000000000,0.007519398448124149821059081233443066594190895557403564453125 sulfur,S,33.967867030000000738709786674007773399353027343750000000000000,0.042520598352131823427502155254842364229261875152587890625000 sulfur,S,35.967081200000002638716978253796696662902832031250000000000000,0.000109991200703943683199964587160479823069181293249130249023 chlorine,Cl,34.968852730000001827193045755848288536071777343750000000000000,0.757594848103037898923162174469325691461563110351562500000000 chlorine,Cl,36.965902640000003032128006452694535255432128906250000000000000,0.242405151896962045565686594272847287356853485107421875000000 argon,Ar,35.967545119999996927617758046835660934448242187500000000000000,0.003336205796380696270847510120916012965608388185501098632813 argon,Ar,37.962732199999997817485564155504107475280761718750000000000000,0.000629799206452999775149304007015871320618316531181335449219 argon,Ar,39.962383121999998536466591758653521537780761718750000000000000,0.996033994997166272078459314798237755894660949707031250000000 potassium,K,38.963706492999996555681718746200203895568847656250000000000000,0.932580526071084436878777523816097527742385864257812500000000 potassium,K,39.963998240000002226679498562589287757873535156250000000000000,0.000117099885242112454345267402722186034225160256028175354004 potassium,K,40.961825263000001484670065110549330711364746093750000000000000,0.067302374043673424131029037198459263890981674194335937500000 calcium,Ca,39.962590919999996685874066315591335296630859375000000000000000,0.969400838426726974006442105746828019618988037109375000000000 calcium,Ca,41.958618100000002471006155246868729591369628906250000000000000,0.006472228417153705684605746739634923869743943214416503906250 calcium,Ca,42.958766199999999457759258802980184555053710937500000000000000,0.001350985058105257227353823701321289263432845473289489746094 calcium,Ca,43.955482199999998726980265928432345390319824218750000000000000,0.020860869278785776348428271376178599894046783447265625000000 calcium,Ca,45.953691999999996653514244826510548591613769531250000000000000,0.000042999524425259849917842214228613784143817611038684844971 calcium,Ca,47.952522889999997346421878319233655929565429687500000000000000,0.001872079294802999303859447621789513505063951015472412109375 scandium,Sc,44.955908600000000774343789089471101760864257812500000000000000,1.000000000000000000000000000000000000000000000000000000000000 titanium,Ti,45.952628300000000649561116006225347518920898437500000000000000,0.082520097588289403889305617667559999972581863403320312500000 titanium,Ti,46.951759299999999086594471009448170661926269531250000000000000,0.074411070671519405350657905273692449554800987243652343750000 titanium,Ti,47.947942300000001125681592384353280067443847656250000000000000,0.737141543014838140912559083517407998442649841308593750000000 titanium,Ti,48.947866300000001160697138402611017227172851562500000000000000,0.054113506379234489751528514034362160600721836090087890625000 titanium,Ti,49.944787300000001550870365463197231292724609375000000000000000,0.051813782346118462951434224805780104361474514007568359375000 vanadium,V,49.947156700000000739692040951922535896301269531250000000000000,0.002503979968160254584302881752932989911641925573348999023438 vanadium,V,50.943957699999998567363945767283439636230468750000000000000000,0.997496020031839680797247638111002743244171142578125000000000 chromium,Cr,49.946042699999999570081854471936821937561035156250000000000000,0.043450743830478963380947732275672024115920066833496093750000 chromium,Cr,51.940506399999996745009411824867129325866699218750000000000000,0.837881075122238416774678171350387856364250183105468750000000 chromium,Cr,52.940648400000000606269168201833963394165039062500000000000000,0.095010483865806516501351097758742980659008026123046875000000 chromium,Cr,53.938879399999997588111000368371605873107910156250000000000000,0.023657697181476075587447382986283628270030021667480468750000 manganese,Mn,54.938044300000001385342329740524291992187500000000000000000000,1.000000000000000000000000000000000000000000000000000000000000 iron,Fe,53.939609300000000757790985517203807830810546875000000000000000,0.058452792721208068904559240763774141669273376464843750000000 iron,Fe,55.934936299999996833776094717904925346374511718750000000000000,0.917532497856775930422656983864726498723030090332031250000000 iron,Fe,56.935393300000001204352884087711572647094726562500000000000000,0.021190743592002535267138085828264593146741390228271484375000 iron,Fe,57.933274300000000778254616307094693183898925781250000000000000,0.002823965830013456732028309659199294401332736015319824218750 cobalt,Co,58.933194399999997870054357917979359626770019531250000000000000,1.000000000000000000000000000000000000000000000000000000000000 nickel,Ni,57.935342300000002069282345473766326904296875000000000000000000,0.680769095231327558970235713786678388714790344238281250000000 nickel,Ni,59.930786300000001176613295683637261390686035156250000000000000,0.262230419610671172669924544607056304812431335449218750000000 nickel,Ni,60.931056300000001613170752534642815589904785156250000000000000,0.011399083035777891892426083586542517878115177154541015625000 nickel,Ni,61.928345399999997766826709266752004623413085937500000000000000,0.036346250253448952882706635136855766177177429199218750000000 nickel,Ni,63.927967399999999997817212715744972229003906250000000000000000,0.009255151868774300419340228529563319170847535133361816406250 copper,Cu,62.929598400000003266541170887649059295654296875000000000000000,0.691494255172344751692037334578344598412513732910156250000000 copper,Cu,64.927790599999994469726516399532556533813476562500000000000000,0.308505744827655137285660202906001359224319458007812500000000 zinc,Zn,63.929142599999998708426574012264609336853027343750000000000000,0.491645713885820234700929631799226626753807067871093750000000 zinc,Zn,65.926034700000002430897438898682594299316406250000000000000000,0.277325508740183801492662496457342058420181274414062500000000 zinc,Zn,66.927128699999997252234607003629207611083984375000000000000000,0.040405292597461665848879164286699960939586162567138671875000 zinc,Zn,67.924845700000005876972863916307687759399414062500000000000000,0.184515103497573135227227680843498092144727706909179687500000 zinc,Zn,69.925321999999994204699760302901268005371093750000000000000000,0.006108381278961075126765489784474993939511477947235107421875 gallium,Ga,68.925574900000000866384652908891439437866210937500000000000000,0.601079797840404217446064194518839940428733825683593750000000 gallium,Ga,70.924703699999994910285749938338994979858398437500000000000000,0.398920202159595671531633342965506017208099365234375000000000 germanium,Ge,69.924249700000004281719157006591558456420898437500000000000000,0.205705812301332946478993335404084064066410064697265625000000 germanium,Ge,71.922075860000006741756806150078773498535156250000000000000000,0.274503726116209989527305879164487123489379882812500000000000 germanium,Ge,72.923459039999997344239091034978628158569335937500000000000000,0.077504017086240106770844704442424699664115905761718750000000 germanium,Ge,73.921177760999995598467648960649967193603515625000000000000000,0.364982406812098314485837136089685373008251190185546875000000 germanium,Ge,75.921402720000003228051355108618736267089843750000000000000000,0.077304037684118531714716482383664697408676147460937500000000 arsenic,As,74.921595699999997464146872516721487045288085937500000000000000,1.000000000000000000000000000000000000000000000000000000000000 selenium,Se,73.922475910000002841115929186344146728515625000000000000000000,0.008938426836876709608015190156038443092256784439086914062500 selenium,Se,75.919213720000001899279595818370580673217773437500000000000000,0.093712506598838590798905556766840163618326187133789062500000 selenium,Se,76.919914259999998762395989615470170974731445312500000000000000,0.076302570747548426055573145276866853237152099609375000000000 selenium,Se,77.917309200000005375841283239424228668212890625000000000000000,0.237686167234566703143627819372341036796569824218750000000000 selenium,Se,79.916522900000003915010893251746892929077148437500000000000000,0.496053694549759227605534306348999962210655212402343750000000 selenium,Se,81.916700099999999906685843598097562789916992187500000000000000,0.087306634032410290746639702774700708687305450439453125000000 bromine,Br,78.918338099999999712963472120463848114013671875000000000000000,0.506898896176611657438115798868238925933837890625000000000000 bromine,Br,80.916290099999997664781403727829456329345703125000000000000000,0.493101103823388231539581738616107031702995300292968750000000 krypton,Kr,77.920365599999996675251168198883533477783203125000000000000000,0.003552948126957346328819165037771199422422796487808227539063 krypton,Kr,79.916378600000001597436494193971157073974609375000000000000000,0.022860666234272977725971998097520554438233375549316406250000 krypton,Kr,81.913483700000000453655957244336605072021484375000000000000000,0.115931407401451927463575941601447993889451026916503906250000 krypton,Kr,82.914127199999995809776009991765022277832031250000000000000000,0.115000220996773441783922464765055337920784950256347656250000 krypton,Kr,83.911497733000004473069566302001476287841796875000000000000000,0.569863179997571966950431487930472940206527709960937500000000 krypton,Kr,85.910610633000004554560291580855846405029296875000000000000000,0.172791577242972227423933873069472610950469970703125000000000 rubidium,Rb,84.911789742999999930361809674650430679321289062500000000000000,0.721691132354705722207199869444593787193298339843750000000000 rubidium,Rb,86.909180535999993821860698517411947250366210937500000000000000,0.278308867645294166770497668039752170443534851074218750000000 strontium,Sr,83.913419899999993845085555221885442733764648437500000000000000,0.005609775608975640752429381308274969342164695262908935546875 strontium,Sr,85.909261900000004175126377958804368972778320312500000000000000,0.098606055757769678349333730693615507334470748901367187500000 strontium,Sr,86.908878900000004819048626814037561416625976562500000000000000,0.070007199712011511372189431767765199765563011169433593750000 strontium,Sr,87.905613900000005855872586835175752639770507812500000000000000,0.825776968921243081922511919401586055755615234375000000000000 yttrium,Y,88.905842000000006919435691088438034057617187500000000000000000,1.000000000000000000000000000000000000000000000000000000000000 zirconium,Zr,89.904702000000000339241523761302232742309570312500000000000000,0.514422711621750239352479638910153880715370178222656250000000 zirconium,Zr,90.905642000000000280124368146061897277832031250000000000000000,0.112234410554393593262290096390643157064914703369140625000000 zirconium,Zr,91.905032000000005609763320535421371459960937500000000000000000,0.171550886397901253266340404479706194251775741577148437500000 zirconium,Zr,93.906311999999999784449755679816007614135742187500000000000000,0.173788376250214926521664438041625544428825378417968750000000 zirconium,Zr,95.908271999999996637598087545484304428100585937500000000000000,0.028003615175739928616627238966430013533681631088256835937500 niobium,Nb,92.906372000000004618414095602929592132568359375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 molybdenum,Mo,91.906808600000005071706254966557025909423828125000000000000000,0.145308494342837241086741073559096548706293106079101562500000 molybdenum,Mo,93.905085299999996095721144229173660278320312500000000000000000,0.091496458524138415957516201615362660959362983703613281250000 molybdenum,Mo,94.905839299999996683254721574485301971435546875000000000000000,0.158387558641321063435114524509117472916841506958007812500000 molybdenum,Mo,95.904676300000005539914127439260482788085937500000000000000000,0.166690329831184980147185115129104815423488616943359375000000 molybdenum,Mo,96.906018299999999499050318263471126556396484375000000000000000,0.095999792030779435014764544575882609933614730834960937500000 molybdenum,Mo,97.905405299999998192106431815773248672485351562500000000000000,0.243900902666405350327494261364336125552654266357421875000000 molybdenum,Mo,99.907472799999993640085449442267417907714843750000000000000000,0.098216463963333416886669624545902479439973831176757812500000 ruthenium,Ru,95.907590299999995409052644390612840652465820312500000000000000,0.055402974808013198682044020415560225956141948699951171875000 ruthenium,Ru,97.905296000000006984009814914315938949584960937500000000000000,0.018726273471579152340993346115283202379941940307617187500000 ruthenium,Ru,98.905934799999997153463482391089200973510742187500000000000000,0.127588609866636532030881312493875157088041305541992187500000 ruthenium,Ru,99.904214800000005425317795015871524810791015625000000000000000,0.126054915071900669465421174209041055291891098022460937500000 ruthenium,Ru,100.905577899999997271152096800506114959716796875000000000000000,0.170586053375378299268305681835045106709003448486328125000000 ruthenium,Ru,101.904344899999998119710653554648160934448242187500000000000000,0.315451225206183960558803391904802992939949035644531250000000 ruthenium,Ru,103.905432000000004677531251218169927597045898437500000000000000,0.186189948200308125203505937861336860805749893188476562500000 rhodium,Rh,102.905501999999998474777385126799345016479492187500000000000000,1.000000000000000000000000000000000000000000000000000000000000 palladium,Pd,101.905602000000001794433046597987413406372070312500000000000000,0.010207550187954890497099569302008603699505329132080078125000 palladium,Pd,103.904031099999997422855813056230545043945312500000000000000000,0.111463248820283120088525663504697149619460105895996093750000 palladium,Pd,104.905080900000001520311343483626842498779296875000000000000000,0.223336399264176588275176982278935611248016357421875000000000 palladium,Pd,105.903480900000005249239620752632617950439453125000000000000000,0.273264416540030363744762098576757125556468963623046875000000 palladium,Pd,107.903892900000002441629476379603147506713867187500000000000000,0.264546508837878890929573572066146880388259887695312500000000 palladium,Pd,109.905172600000000215914042200893163681030273437500000000000000,0.117181876349676070137029171291942475363612174987792968750000 silver,Ag,106.905091999999996232872945256531238555908203125000000000000000,0.518389668985958174118877650471404194831848144531250000000000 silver,Ag,108.904755100000002698834578040987253189086914062500000000000000,0.481610331014041714858819887012941762804985046386718750000000 cadmium,Cd,105.906460899999999014653440099209547042846679687500000000000000,0.012567197514954164816458614950533956289291381835937500000000 cadmium,Cd,107.904183900000006701702659483999013900756835937500000000000000,0.008928009053980960965657409644791187020018696784973144531250 cadmium,Cd,109.903007400000007010021363385021686553955078125000000000000000,0.124890149496662231087817929164884844794869422912597656250000 cadmium,Cd,110.904183399999993753226590342819690704345703125000000000000000,0.127983459688489453753845737082883715629577636718750000000000 cadmium,Cd,111.902763399999997773193172179162502288818359375000000000000000,0.241267197414976458658131264201074372977018356323242187500000 cadmium,Cd,112.904408300000000053842086344957351684570312500000000000000000,0.122184752800125570604272695618419675156474113464355468750000 cadmium,Cd,113.903365300000004367575456853955984115600585937500000000000000,0.287277937020044504823346187549759633839130401611328125000000 cadmium,Cd,115.904763200000004985668056178838014602661132812500000000000000,0.074901297010766587636254598692175932228565216064453125000000 indium,In,112.904062699999997221311787143349647521972656250000000000000000,0.042954845418549769675564675708301365375518798828125000000000 indium,In,114.903878789000003735054633580148220062255859375000000000000000,0.957045154581450119302132861776044592261314392089843750000000 tin,Sn,111.904824399999995421239873394370079040527343750000000000000000,0.009707379007667929146641050408561568474397063255310058593750 tin,Sn,113.902783700000000521868059877306222915649414062500000000000000,0.006608215781738930282018795736576066701672971248626708984375 tin,Sn,114.903344709999998940475052222609519958496093750000000000000000,0.003409079548521898664348306340343697229400277137756347656250 tin,Sn,115.901743100000004460525815375149250030517578125000000000000000,0.145370749897527656857576516813423950225114822387695312500000 tin,Sn,116.902954300000004650428309105336666107177734375000000000000000,0.076859248003039171148742525474517606198787689208984375000000 tin,Sn,117.901607299999994893369148485362529754638671875000000000000000,0.242144620952342848330118840749491937458515167236328125000000 tin,Sn,118.903311599999994996323948726058006286621093750000000000000000,0.085916802463334898676272644024720648303627967834472656250000 tin,Sn,119.902202700000003687819116748869419097900390625000000000000000,0.325722055045137792728127124064485542476177215576171875000000 tin,Sn,121.903441999999998301973391789942979812622070312500000000000000,0.046317494276545329023875297025369945913553237915039062500000 tin,Sn,123.905277799999993249002727679908275604248046875000000000000000,0.057944355024143474885978122301821713335812091827392578125000 antimony,Sb,120.903812000000002058186510112136602401733398437500000000000000,0.572091349038115315472907695948379114270210266113281250000000 antimony,Sb,122.904212000000001125954440794885158538818359375000000000000000,0.427908650961884573504789841535966843366622924804687500000000 tellurium,Te,119.904061999999996146470948588103055953979492187500000000000000,0.000909764371027903685079651907585684966761618852615356445313 tellurium,Te,121.903041000000001758962753228843212127685546875000000000000000,0.025505394102927340937991829150632838718593120574951171875000 tellurium,Te,122.904270999999994273821357637643814086914062500000000000000000,0.008927687728878220055350745099076448241248726844787597656250 tellurium,Te,123.902821000000002982233127113431692123413085937500000000000000,0.047401722953754971134898710261040832847356796264648437500000 tellurium,Te,124.904431000000002427441359031945466995239257812500000000000000,0.070696689557404629455916733604681212455034255981445312500000 tellurium,Te,125.903311000000002195520210079848766326904296875000000000000000,0.188376210561464557668998054396070074290037155151367187500000 tellurium,Te,127.904461699999998813837009947746992111206054687500000000000000,0.317407791382032011817670991149498149752616882324218750000000 tellurium,Te,129.906222759000002042739652097225189208984375000000000000000000,0.340774739342510235573513455165084451436996459960937500000000 iodine,I,126.904472999999995863618096336722373962402343750000000000000000,1.000000000000000000000000000000000000000000000000000000000000 xenon,Xe,123.905891999999994368408806622028350830078125000000000000000000,0.000952296533640617525774685336870106766582466661930084228516 xenon,Xe,125.904302999999998746716300956904888153076171875000000000000000,0.000890196759683794711613680217254795934422872960567474365234 xenon,Xe,127.903531799999996110273059457540512084960937500000000000000000,0.019102830465697103606848017420816177036613225936889648437500 xenon,Xe,128.904780864000002793545718304812908172607421875000000000000000,0.264005869018636762923790683998959138989448547363281250000000 xenon,Xe,129.903509409999998069906723685562610626220703125000000000000000,0.040709981815666186621971434078659513033926486968994140625000 xenon,Xe,130.905084200000004557296051643788814544677734375000000000000000,0.212323527142361190289676642350968904793262481689453125000000 xenon,Xe,131.904155094000003600740456022322177886962890625000000000000000,0.269085350529324029977829013660084456205368041992187500000000 xenon,Xe,133.905395700000013903263607062399387359619140625000000000000000,0.104356830141138279266499466757522895932197570800781250000000 xenon,Xe,135.907214487999993934863596223294734954833984375000000000000000,0.088573117593851946605099101361702196300029754638671875000000 caesium,Cs,132.905451967000004742658347822725772857666015625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 barium,Ba,129.906321999999988747731549665331840515136718750000000000000000,0.001060985146207953045902061539607075246749445796012878417969 barium,Ba,131.905061799999998584098648279905319213867187500000000000000000,0.001010985846198153050023993415607037604786455631256103515625 barium,Ba,133.904508200000009310315363109111785888671875000000000000000000,0.024171461599537605313692267827718751505017280578613281250000 barium,Ba,134.905688199999985954491421580314636230468750000000000000000000,0.065920277116120362670415033790050074458122253417968750000000 barium,Ba,135.904576200000008157076081261038780212402343750000000000000000,0.078541300421794094099858796198532218113541603088378906250000 barium,Ba,136.905827200000004495450411923229694366455078125000000000000000,0.112320827508414877726750091824214905500411987304687500000000 barium,Ba,137.905247199999990925789461471140384674072265625000000000000000,0.716974162361726841119491382414707913994789123535156250000000 lanthanum,La,137.907123000000012780219549313187599182128906250000000000000000,0.000888171872103250392010975744483403104823082685470581054688 lanthanum,La,138.906362000000001444277586415410041809082031250000000000000000,0.999111828127896672846475212281802669167518615722656250000000 cerium,Ce,135.907129300000008242932381108403205871582031250000000000000000,0.001851973331584025024912354417949700291501358151435852050781 cerium,Ce,137.905998000000010961230145767331123352050781250000000000000000,0.002511963827720880421123794690174690913408994674682617187500 cerium,Ce,139.905441999999993640813045203685760498046875000000000000000000,0.884492463308528265031327464384958148002624511718750000000000 cerium,Ce,141.909252000000009275026968680322170257568359375000000000000000,0.111143599532166723053983048430382041260600090026855468750000 praseodymium,Pr,140.907661999999987756382324732840061187744140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 neodymium,Nd,141.907732000000009975337889045476913452148437500000000000000000,0.271519166958828106483991859931848011910915374755859375000000 neodymium,Nd,142.909821999999991248841979540884494781494140625000000000000000,0.121740433020292235233306143982190405949950218200683593750000 neodymium,Nd,143.910091999999991685399436391890048980712890625000000000000000,0.237977663997580829446931716120161581784486770629882812500000 neodymium,Nd,144.912581999999986237526172772049903869628906250000000000000000,0.082929723850915446070608538775559281930327415466308593750000 neodymium,Nd,145.913121999999987110641086474061012268066406250000000000000000,0.171890140355501652713599014532519504427909851074218750000000 neodymium,Nd,147.916901999999993222445482388138771057128906250000000000000000,0.057561075412857647115583148433870519511401653289794921875000 neodymium,Nd,149.920902000000012321834219619631767272949218750000000000000000,0.056381796404024006608146635244338540360331535339355468750000 samarium,Sm,143.912012000000004263711161911487579345703125000000000000000000,0.030772522277086666181444840617587033193558454513549804687500 samarium,Sm,146.914902000000012094460544176399707794189453125000000000000000,0.149881578776357327065227309503825381398200988769531250000000 samarium,Sm,147.914831999999989875504979863762855529785156250000000000000000,0.112382691006085513873991033051424892619252204895019531250000 samarium,Sm,148.917192000000000007275957614183425903320312500000000000000000,0.138246406123312015612469849656918086111545562744140625000000 samarium,Sm,149.917282000000000152795109897851943969726562500000000000000000,0.073792068527347848272412988990254234522581100463867187500000 samarium,Sm,151.919742000000013604221749119460582733154296875000000000000000,0.267451009404714612482933944193064235150814056396484375000000 samarium,Sm,153.922222000000004982211976312100887298583984375000000000000000,0.227473723885095902019770619517657905817031860351562500000000 europium,Eu,150.919861999999994850440998561680316925048828125000000000000000,0.478103065570820051632949798658955842256546020507812500000000 europium,Eu,152.921242000000006555637810379266738891601562500000000000000000,0.521896934429179837344747738825390115380287170410156250000000 gadolinium,Gd,151.919802000000004227331373840570449829101562500000000000000000,0.002009636255837693018938550082452820788603276014328002929688 gadolinium,Gd,153.920872000000002799424692057073116302490234375000000000000000,0.021826049485043207132317633067941642366349697113037109375000 gadolinium,Gd,154.922631999999993013261700980365276336669921875000000000000000,0.147985214676143617129611129712429828941822052001953125000000 gadolinium,Gd,155.922132000000004836692824028432369232177734375000000000000000,0.204672954195290635048820604424690827727317810058593750000000 gadolinium,Gd,156.923971999999992021912476047873497009277343750000000000000000,0.156491675006823760529783839956508018076419830322265625000000 gadolinium,Gd,157.924112000000008038114174269139766693115234375000000000000000,0.248435033258980114689862261911912355571985244750976562500000 gadolinium,Gd,159.927062000000006491973181255161762237548828125000000000000000,0.218579437121880937322515592313720844686031341552734375000000 terbium,Tb,158.925352000000003727109287865459918975830078125000000000000000,1.000000000000000000000000000000000000000000000000000000000000 dysprosium,Dy,155.924282000000005155015969648957252502441406250000000000000000,0.000562985756460361477619691594753703611786477267742156982422 dysprosium,Dy,157.924421999999992749508237466216087341308593750000000000000000,0.000952975889709990254573812595850768047966994345188140869141 dysprosium,Dy,159.925202000000012958480510860681533813476562500000000000000000,0.023291210732368467645203580218549177516251802444458007812500 dysprosium,Dy,160.926941999999996824044501408934593200683593750000000000000000,0.188889421097646226233024435714469291269779205322265625000000 dysprosium,Dy,161.926812000000012403688742779195308685302734375000000000000000,0.254747154896981076177553404704667627811431884765625000000000 dysprosium,Dy,162.928741999999999734427547082304954528808593750000000000000000,0.248957901365095435330943018925609067082405090332031250000000 dysprosium,Dy,163.929181999999997287886799313127994537353515625000000000000000,0.282598350261738351374418698469526134431362152099609375000000 holmium,Ho,164.930331999999992831362760625779628753662109375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 erbium,Er,161.928791999999987183400662615895271301269531250000000000000000,0.001395973476503946332158423437874716910300776362419128417969 erbium,Er,163.929212000000006810296326875686645507812500000000000000000000,0.016012695758780580435054474719436257146298885345458984375000 erbium,Er,165.930302000000011730662663467228412628173828125000000000000000,0.335027234482544788995994622382568195462226867675781250000000 erbium,Er,166.932051999999998770363163203001022338867187500000000000000000,0.228686654953555862368475004586798604577779769897460937500000 erbium,Er,167.932381999999989830030244775116443634033203125000000000000000,0.269776674243189351631855288360384292900562286376953125000000 erbium,Er,169.935472000000004300090949982404708862304687500000000000000000,0.149100767085425356395234075534972362220287322998046875000000 thulium,Tm,168.934222000000005436959327198565006256103515625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 ytterbium,Yb,167.933891999999985955582815222442150115966796875000000000000000,0.001232929969577727796758992440118163358420133590698242187500 ytterbium,Yb,169.934772000000009484210750088095664978027343750000000000000000,0.029822206098693591902470956256365752778947353363037109375000 ytterbium,Yb,170.936331999999993058736436069011688232421875000000000000000000,0.140905996539396560773838018576498143374919891357421875000000 ytterbium,Yb,171.936392000000012103555491194128990173339843750000000000000000,0.216800685721051017429417129278590437024831771850585937500000 ytterbium,Yb,172.938221999999996114638634026050567626953125000000000000000000,0.161027253651992552363481081556528806686401367187500000000000 ytterbium,Yb,173.938872000000003481545718386769294738769531250000000000000000,0.320249909805123023076589561242144554853439331054687500000000 ytterbium,Yb,175.942581999999987374394549988210201263427734375000000000000000,0.129961018214165419104588750087714288383722305297851562500000 lutetium,Lu,174.940782000000012885720934718847274780273437500000000000000000,0.974008767577204226384424146090168505907058715820312500000000 lutetium,Lu,175.942691999999993868186720646917819976806640625000000000000000,0.025991232422795697287742910930319339968264102935791015625000 hafnium,Hf,173.940052000000008547431207261979579925537109375000000000000000,0.001609652315099938373749166586890169128309935331344604492188 hafnium,Hf,175.941412000000013904355000704526901245117187500000000000000000,0.052668623577307296934613134453684324398636817932128906250000 hafnium,Hf,176.943231999999994741301634348928928375244140625000000000000000,0.185969830516608397585898160286888014525175094604492187500000 hafnium,Hf,177.943712000000004991306923329830169677734375000000000000000000,0.272821070648739838482299546740250661969184875488281250000000 hafnium,Hf,178.945821999999992613084032200276851654052734375000000000000000,0.136190582834107815068946933934057597070932388305664062500000 hafnium,Hf,179.946562000000000125510268844664096832275390625000000000000000,0.350740240108136591690168870627530850470066070556640625000000 tantalum,Ta,179.947462000000001580701791681349277496337890625000000000000000,0.000120131992311552486551486096377772128107608295977115631104 tantalum,Ta,180.948002000000002453816705383360385894775390625000000000000000,0.999879868007688354936135510797612369060516357421875000000000 tungsten,W,179.946711999999990894139045849442481994628906250000000000000000,0.001209872963338849303702171589236513682408258318901062011719 tungsten,W,181.948204699999990907599567435681819915771484375000000000000000,0.264988176241494621798722164385253563523292541503906250000000 tungsten,W,182.950223700000009330324246548116207122802734375000000000000000,0.143124971877952811283307710255030542612075805664062500000000 tungsten,W,183.950931700000012369855539873242378234863281250000000000000000,0.306387829277925793913794905165559612214565277099609375000000 tungsten,W,185.954362000000003263266989961266517639160156250000000000000000,0.284289149639287863635672692907974123954772949218750000000000 rhenium,Re,184.952955900000006295158527791500091552734375000000000000000000,0.374005039798408045470523575204424560070037841796875000000000 rhenium,Re,186.955750999999992245648172684013843536376953125000000000000000,0.625994960201591843507173962279921397566795349121093750000000 osmium,Os,183.952489100000008193092071451246738433837890625000000000000000,0.000209947723016968765524098428087995671376120299100875854492 osmium,Os,185.953841000000011263182386755943298339843750000000000000000000,0.015926034417430057904541129687459033448249101638793945312500 osmium,Os,186.955750999999992245648172684013843536376953125000000000000000,0.019615115836156795520173190539026109036058187484741210937500 osmium,Os,187.955840999999992391167324967682361602783203125000000000000000,0.132457018202467580181291850749403238296508789062500000000000 osmium,Os,188.958142000000009375071385875344276428222656250000000000000000,0.161519781574387955025429164379602298140525817871093750000000 osmium,Os,189.958441999999990912328939884901046752929687500000000000000000,0.262554623898649197588639481182326562702655792236328125000000 osmium,Os,191.961481999999989511707099154591560363769531250000000000000000,0.407717478347891348899878494194126687943935394287109375000000 iridium,Ir,190.960591999999991230652085505425930023193359375000000000000000,0.373050779688124722888176165724871680140495300292968750000000 iridium,Ir,192.962921999999991840013535693287849426269531250000000000000000,0.626949220311875166089521371759474277496337890625000000000000 platinum,Pt,189.959934000000004061803338117897510528564453125000000000000000,0.000121987349911814132899338936066868654961581341922283172607 platinum,Pt,191.961041999999991958247846923768520355224609375000000000000000,0.007821588901230941415221309398475568741559982299804687500000 platinum,Pt,193.962681699999990314609021879732608795166015625000000000000000,0.328605923565726210089366077227168716490268707275390625000000 platinum,Pt,194.964792700000003833338269032537937164306640625000000000000000,0.337788971283677852408544595164130441844463348388671875000000 platinum,Pt,195.964952699999997776103555224835872650146484375000000000000000,0.252107856415289710572125159160350449383258819580078125000000 platinum,Pt,197.967892000000006191839929670095443725585937500000000000000000,0.073553672484163390432598816914833150804042816162109375000000 gold,Au,196.966569600000013906537787988781929016113281250000000000000000,1.000000000000000000000000000000000000000000000000000000000000 mercury,Hg,195.965832000000006019035936333239078521728515625000000000000000,0.001509815802472098391837085351596670079743489623069763183594 mercury,Hg,197.966769300000009934592526406049728393554687500000000000000000,0.099707835644051417967048678292485419660806655883789062500000 mercury,Hg,198.968281300000001010630512610077857971191406250000000000000000,0.168701418426951910145561441822792403399944305419921875000000 mercury,Hg,199.968327299999998558632796630263328552246093750000000000000000,0.230990819120067331082779560347262304276227951049804687500000 mercury,Hg,200.970303599999994048630469478666782379150390625000000000000000,0.131793921141620695713925215386552736163139343261718750000000 mercury,Hg,201.970643599999988282434060238301753997802734375000000000000000,0.298589572072207154462830658303573727607727050781250000000000 mercury,Hg,203.973494299999998702332959510385990142822265625000000000000000,0.068706617792629293139938795320631470531225204467773437500000 thallium,Tl,202.972345100000012507734936662018299102783203125000000000000000,0.295204095918081610427918803907232359051704406738281250000000 thallium,Tl,204.974428100000011454540072008967399597167968750000000000000000,0.704795904081918278549778733577113598585128784179687500000000 lead,Pb,203.973044899999990775540936738252639770507812500000000000000000,0.014094362255097959285565778486670751590281724929809570312500 lead,Pb,205.974466900000010127769201062619686126708984375000000000000000,0.241003598560575765796798464180028531700372695922851562500000 lead,Pb,206.975897900000006757181836292147636413574218750000000000000000,0.221011595361855245345239495691203046590089797973632812500000 lead,Pb,207.976653900000002295200829394161701202392578125000000000000000,0.523890443822470963652904174523428082466125488281250000000000 bismuth,Bi,208.980401000000000522049958817660808563232421875000000000000000,1.000000000000000000000000000000000000000000000000000000000000 uranium,U,234.040952000000004318280844017863273620605468750000000000000000,0.000054599923560107009460132254652364736102754250168800354004 uranium,U,235.043932000000012294549378566443920135498046875000000000000000,0.007204689913434121108226637630878030904568731784820556640625 uranium,U,238.050792000000001280568540096282958984375000000000000000000000,0.992740710163005690702675565262325108051300048828125000000000 thorium,Th,232.038061999999996487531461752951145172119140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 protactinium,Pa,231.035881999999986646798788569867610931396484375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/leucine.svg000664 001750 001750 00000003204 15100504560 034620 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/lysine.svg000664 001750 001750 00000003742 15100504560 034506 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/methionine.svg000664 001750 001750 00000003775 15100504560 035271 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/modification_dictionary000664 001750 001750 00000003227 15100504560 037216 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the modification_dictionary file where the correspondences # between the name of each modification and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. Also, the graphical operation that is to # be performed upon chemical modification of a monomer is listed ('T' # for transparent and 'O' for opaque). See the manual for details. # The format of the file is like this : # ------------------------------------- # Phosphorylation%T%phospho.svg # where Phosphorylation is the name of the modification. T indicates # that the visual rendering of the modification is a transparent # process (O indicates that the visual rendering of the modification # is a full image replacement 'O' like opaque). phospho.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Phosphorylation%T%phospho.svg Sulphation%T%sulpho.svg AmidationAsp%O%asparagine.svg Acetylation%T%acetyl.svg Glutamylation%T%glutamylation.svg Glycylation%T%glycylation.svg AmidationGlu%O%glutamine.svg Oxidation%T%oxidation.svg SulfideBond%T%sulfbond.svg ProtonLoss%T%protonloss.svg Hydroxylation%T%hydroxylation.svg tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/monomer_dictionary000664 001750 001750 00000002377 15100504560 036232 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the monomer_dictionary file where the correspondences # between the codes of each monomer and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. # The format of the file is like this : # ------------------------------------- # A%alanine.svg # where A is the monomer code and alanine.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. A%alanine.svg C%cysteine.svg D%aspartate.svg E%glutamate.svg F%phenylalanine.svg G%glycine.svg H%histidine.svg I%isoleucine.svg K%lysine.svg L%leucine.svg M%methionine.svg N%asparagine.svg P%proline.svg Q%glutamine.svg R%arginine.svg S%serine.svg T%threonine.svg V%valine.svg W%tryptophan.svg Y%tyrosine.svg O%diaminopimelic.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/oxidation.svg000664 001750 001750 00000005727 15100504560 035206 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/pdb-code-conversion.dic000664 001750 001750 00000000506 15100504560 036717 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# Converts from code on the left of '>' to code on the right. # Number of letters allowed in each code is # described with syntax 1>3 and that line should be the first # non-comment line in this file. 3>1 ALA>A CYS>C ASP>D GLU>E PHE>F GLY>G HIS>H ILE>I LYS>K LEU>L MET>M ASN>N PRO>P GLN>Q ARG>R SER>S THR>T VAL>V TRP>W TYR>Y tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/phenylalanine.svg000664 001750 001750 00000003344 15100504560 035751 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/phospho.svg000664 001750 001750 00000002365 15100504560 034663 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/pka_ph_pi.xml000664 001750 001750 00000031470 15100504560 035135 0ustar00rusconirusconi000000 000000 ]> A N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped C N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral SH2 8.3 FALSE never_trapped D N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral COOH 3.65 FALSE never_trapped MONOMER_MODIF AmidationAsp LOST E N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral COOH 4.25 FALSE never_trapped MONOMER_MODIF AmidationGlu LOST F N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped G N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped H N-term NH2 9.6 TRUE left_trapped C-term COOH 2.36 FALSE right_trapped In-ring NH+ 6 TRUE never_trapped I N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped K N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral NH2 10.53 TRUE never_trapped MONOMER_MODIF Acetylation LOST L N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped M N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped N N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped P N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Q N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped R N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral guanidinium 12.48 TRUE never_trapped S N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral alcohol 13 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST T N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Lateral alcohol 13 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST V N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped W N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.35 FALSE right_trapped Y N-term NH2 9.6 TRUE left_trapped LE_PLM_MODIF Acetylation LOST C-term COOH 2.36 FALSE right_trapped Lateral phenol 10.1 FALSE never_trapped MONOMER_MODIF Phosphorylation LOST Phosphorylation none_set 1.2 FALSE none_set 6.5 FALSE libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/proline.svg000664 001750 001750 00000003677 15100504560 034662 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/protein-1-letter.xml000664 001750 001750 00000025720 15100504560 036240 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> protein-1-letter "N-term amine"+H "C-term carboxylic acid"+OH 1 +H 1 1 Alanine A C3H5N1O1 Cysteine C C3H5N1O1S1 Aspartate D C4H5N1O3 Glutamate E C5H7N1O3 Phenylalanine F C9H9N1O1 Glycine G C2H3N1O1 Histidine H C6H7N3O1 Isoleucine I C6H11N1O1 Lysine K C6H12N2O1 Leucine L C6H11N1O1 Methionine M C5H9N1O1S1 Asparagine N C4H6N2O2 DiaminoPimelic O C7H12N2O3 Proline P C5H7N1O1 Glutamine Q C5H8N2O2 Arginine R C6H12N4O1 Serine S C3H5N1O2 Threonine T C4H7N1O2 Valine V C5H9N1O1 Tryptophan W C11H10N2O1 Tyrosine Y C9H9N1O2 Acetylation C2H2O1 ;K; 1 AmidationAsp H1N1-O1 ;D; 1 AmidationGlu H1N1-O1 ;E; 1 Carbamidomethylation C2H3N1O1 ;C; 1 Carbamylation C1H1N1O1 ;K; 1 CarboxyMethylation C2H2O2 ;C; 1 Chromo-H -H1 ;G; 1 Chromo-H3 -H3 ;Y;F;W; 1 Chromo-O -O1 ;T;S;G; 1 DTNB (adsorbed) C14H8N2O8S2 ;C; 1 Dehydroalanine -H2S1 ;C; 1 Dehydroxylation -H1O1 ;D;E; 1 Formylation C1O1 * 1 GlcNAcMurNAc(R) C19H32N2O12 * 1 Glutamylation C5H7N1O3 ;E; 6 Glycylation C2H3N1O1 ;E; 34 Hydroxylation H1O1 ;F; 1 LysMethylation C1H2 ;K; 3 Methylation C1H2 ;K;R; 2 Oxidation O1 ;M;Y; 1 Phosphorylation H1O3P1 ;S;T;Y; 1 ProtonLoss -H1 ;C;Y;K; 1 SPITC C7H5N1O3S2 * 1 SulfideBond -H2 ;C; 1 Sulphation O3S1 ;S;T;Y; 1 TNB C7H3N1O4S1 ;C; 1 CFP-chromophore Chromo-O Chromo-H3 Chromo-H DisulfideBond ProtonLoss ProtonLoss Chymotrypsin W/;V/ CyanogenBromide M/ Homoseryl M -CH2S+O EndoAspN /D EndoAspN+GluN /D;/E EndoAspN-Trypsin /D;K/;R/;-K/P EndoLysC K/ GluC E/ Trypsin K/;R/;-K/P a LE -C1O1H1 0 b LE -H 0 c LE +N1H2 0 that's just a comment imm NE -C1O1 0 x RE +C1O1-H1 0 y RE +H1 0 z RE -N1H1 0 Typically in ECD of protonated ions tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/protonloss.svg000664 001750 001750 00000004266 15100504560 035350 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/serine.svg000664 001750 001750 00000005123 15100504560 034463 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/sulfbond.svg000664 001750 001750 00000011620 15100504560 035011 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/sulpho.svg000664 001750 001750 00000006334 15100504560 034515 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/threonine.svg000664 001750 001750 00000003612 15100504560 035172 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/tryptophan.svg000664 001750 001750 00000003443 15100504560 035332 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/tyrosine.svg000664 001750 001750 00000003310 15100504560 035046 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-1-letter/valine.svg000664 001750 001750 00000003240 15100504560 034452 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/000775 001750 001750 00000000000 15100507137 032644 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/acetyl.svg000664 001750 001750 00000006234 15100504560 034650 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/alanine.svg000664 001750 001750 00000004304 15100504560 034772 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/arginine.svg000664 001750 001750 00000004366 15100504560 035167 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/asparagine.svg000664 001750 001750 00000004523 15100504560 035421 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/aspartate.svg000664 001750 001750 00000005060 15100504560 035270 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/chemPad.conf000664 001750 001750 00000014633 15100504560 035060 0ustar00rusconirusconi000000 000000 # This is the layout for the chempad of the molecular calculator 'extremecalc' # in the massXpert software program. # Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Filippo Rusconi. # GNU General Public License. # The format of this file allows the definition of colors # according to the following syntax: # # color%OneColor%xxx,yyy,zzz # # with xxx, yyy and zzz numerical values in range [0,255] for the red, # green and blue component of the color respectively. # # Available colors are: white, black, red, darkRed, green, darkGreen, # blue, darkBlue, cyan, darkCyan, magenta, darkMagenta, yellow, # darkYellow, gray, darkGray, lightGray, please, see # http://www.w3.org/TR/SVG/types.html#ColorKeywords for a list of # commonly used colors associated to their rgb value. # # A separator might be used between sections of buttons. Its syntax is: # Note the double && that acts as an escape of the &. # # chempadsep%Hexoses && Fucose%[midnightblue] # # The color specification (%[midnightblue]) is not compulsory # General button syntax is like the example below: # # chempadkey%hydrate%+H2O1%adds a water molecule%[red,blue] # # Each line is divided into 4 compulsory elements: # 'chempadkey' starts the line # 'hydrate' is the label of the button # '+H2O1' is the formula that is applied when the button gets clicked # 'adds a water molecule' is the text to display in the tooltip # '[red,blue]' are the background and foreground colors, respectively # # The colors specification (last %[color,color]) is not compulsory # # The geometry is specified by the number of columns of buttons: # chempad_columns%3 would specify that all the buttons in the # chemical pad should be laid out using three columns and as # many rows as necessary to display them all. chempad_columns%3 color%aliceblue%240,248,255 color%antiquewhite%250,235,215 color%aqua%0,255,255 color%aquamarine%127,255,212 color%azure%240,255,255 color%beige%245,245,220 color%bisque%255,228,196 color%black%0,0,0 color%blanchedalmond%255,235,205 color%blue%0,0,255 color%blueviolet%138,43,226 color%brown%165,42,42 color%burlywood%222,184,135 color%cadetblue%95,158,160 color%chartreuse%127,255,0 color%chocolate%210,105,30 color%coral%255,127,80 color%cornflowerblue%100,149,237 color%cornsilk%255,248,220 color%crimson%220,20,60 color%cyan%0,255,255 color%darkblue%0,0,139 color%darkcyan%0,139,139 color%darkgoldenrod%184,134,11 color%darkgray%169,169,169 color%darkgreen%0,100,0 color%darkgrey%169,169,169 color%darkkhaki%189,183,107 color%darkmagenta%139,0,139 color%darkolivegreen%85,107,47 color%darkorange%255,140,0 color%darkorchid%153,50,204 color%darkred%139,0,0 color%darksalmon%233,150,122 color%darkseagreen%143,188,143 color%darkslateblue%72,61,139 color%darkslategray%47,79,79 color%darkslategrey%47,79,79 color%darkturquoise%0,206,209 color%darkviolet%148,0,211 color%deeppink%255,20,147 color%deepskyblue%0,191,255 color%dimgray%105,105,105 color%dimgrey%105,105,105 color%dodgerblue%30,144,255 color%firebrick%178,34,34 color%floralwhite%255,250,240 color%forestgreen%34,139,34 color%fuchsia%255,0,255 color%gainsboro%220,220,220 color%ghostwhite%248,248,255 color%gold%255,215,0 color%goldenrod%218,165,32 color%gray%128,128,128 color%grey%128,128,128 color%green%0,128,0 color%greenyellow%173,255,47 color%honeydew%240,255,240 color%hotpink%255,105,180 color%indianred%205,92,92 color%indigo%75,0,130 color%ivory%255,255,240 color%khaki%240,230,140 color%lavender%230,230,250 color%lavenderblush%255,240,245 color%lawngreen%124,252,0 color%lemonchiffon%255,250,205 color%lightblue%173,216,230 color%lightcoral%240,128,128 color%lightcyan%224,255,255 color%lightgoldenrodyellow%250,250,210 color%lightgray%211,211,211 color%lightgreen%144,238,144 color%lightgrey%211,211,211 color%lightpink%255,182,193 color%lightsalmon%255,160,122 color%lightseagreen%32,178,170 color%lightskyblue%135,206,250 color%lightslategray%119,136,153 color%lightslategrey%119,136,153 color%lightsteelblue%176,196,222 color%lightyellow%255,255,224 color%lime%0,255,0 color%limegreen%50,205,50 color%linen%250,240,230 color%magenta%255,0,255 color%maroon%128,0,0 color%mediumaquamarine%102,205,170 color%mediumblue%0,0,205 color%mediumorchid%186,85,211 color%mediumpurple%147,112,219 color%mediumseagreen%60,179,113 color%mediumslateblue%123,104,238 color%mediumspringgreen%0,250,154 color%mediumturquoise%72,209,204 color%mediumvioletred%199,21,133 color%midnightblue%25,25,112 color%mintcream%245,255,250 color%mistyrose%255,228,225 color%moccasin%255,228,181 color%navajowhite%255,222,173 color%navy%0,0,128 color%oldlace%253,245,230 color%olive%128,128,0 color%olivedrab%107,142,35 color%orange%255,165,0 color%orangered%255,69,0 color%orchid%218,112,214 color%palegoldenrod%238,232,170 color%palegreen%152,251,152 color%paleturquoise%175,238,238 color%palevioletred%219,112,147 color%papayawhip%255,239,213 color%peachpuff%255,218,185 color%peru%205,133,63 color%pink%255,192,203 color%plum%221,160,221 color%powderblue%176,224,230 color%purple%128,0,128 color%red%255,0,0 color%rosybrown%188,143,143 color%royalblue%65,105,225 color%saddlebrown%139,69,19 color%salmon%250,128,114 color%sandybrown%244,164,96 color%seagreen%46,139,87 color%seashell%255,245,238 color%sienna%160,82,45 color%silver%192,192,192 color%skyblue%135,206,235 color%slateblue%106,90,205 color%slategray%112,128,144 color%slategrey%112,128,144 color%snow%255,250,250 color%springgreen%0,255,127 color%steelblue%70,130,180 color%tan%210,180,140 color%teal%0,128,128 color%thistle%216,191,216 color%tomato%255,99,71 color%turquoise%64,224,208 color%violet%238,130,238 color%wheat%245,222,179 color%white%255,255,255 color%whitesmoke%245,245,245 color%yellow%255,255,0 color%yellowgreen%154,205,50 chempad_columns%3 chempadgroup%Generic chempadkey%protonate%+H1%adds a proton chempadkey%hydrate%+H2O1%adds a water molecule chempadkey%0H-ylate%+O1H1%adds an hydroxyl group chempadkey%acetylate%-H1+C2H3O1%adds an acetyl group chempadkey%phosphorylate%-H+H2PO3%add a phosphate group chempadkey%sulfide bond%-H2%oxydizes with loss of hydrogen chempadgroup%Hexoses%[seagreen] chempadkey%Res-GlucNAc%+C8H14N1O6%residue GlcNAc%[lawngreen,black] chempadkey%Res-Lactoyl-MurNAc%+C11H17N1O6%residue MurNAc+Lactoyl%[lawngreen,black] chempadgroup%Step residues%[tomato] chempadkey%Res-mDAP%+C7H12N2O3%residue mDAP%[tomato,black] chempadkey%Res-iGlu%+C5H7N1O3%residue iGlu%[tomato,black] chempadkey%Res-Ala%+C3H5N1O1%residue Ala%[tomato,black] chempadkey%Res-iLys%+C6H12N2O2%residue iLys%[tomato,black] tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/cross_linker_dictionary000664 001750 001750 00000002204 15100504560 037425 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007,2008 Filippo Rusconi # This is the cross_linker_dictionary file where the correspondences # between the name of each cross-link and their graphic file (svg file # called "image") used to graphicallly render them in the sequence # editor are made. See the manual for details. # The format of the file is like this : # ------------------------------------- # DisulfideBond%disulfidebond.svg # where DisulfideBond is the name of the cross-link. disulfidebond.svg # is a resolution-independent svg file to be used to render the # cross-link vignette. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. DisulfideBond%disulfidebond-cross-link.svg CFP-chromophore%cfp-chromophore.svg libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/cursor.svg000664 001750 001750 00000002144 15100504560 034700 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/cysteine.svg000664 001750 001750 00000005167 15100504560 035216 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/default-cross-link-vignette.svg000664 001750 001750 00000013740 15100504560 040641 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/default-modif-vignette.svg000664 001750 001750 00000017145 15100504560 037656 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/glutamate.svg000664 001750 001750 00000004000 15100504560 035260 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/glutamine.svg000664 001750 001750 00000004002 15100504560 035264 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/glycine.svg000664 001750 001750 00000004106 15100504560 035015 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/histidine.svg000664 001750 001750 00000004101 15100504560 035257 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/isoleucine.svg000664 001750 001750 00000003340 15100504560 035442 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/isotopic-data.dat000664 001750 001750 00000116230 15100504560 036017 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file contains isotopic data in a format that can accommodate # comments in the form of lines beginning with the '#' character. hydrogen,H,1.007825032269999976364260874106548726558685302734375000000000,0.999884290164307909520857720053754746913909912109375000000000 hydrogen,H,2.014101778190000135992931973305530846118927001953125000000000,0.000115709835692033314582735648023970043141162022948265075684 helium,He,3.016029322000000068015879151062108576297760009765625000000000,0.000001342999991941999914655050951672876635711872950196266174 helium,He,4.002603254140000288430201180744916200637817382812500000000000,0.999998657000008006612290500925155356526374816894531250000000 lithium,Li,6.015122887100000426130463893059641122817993164062500000000000,0.075933925285977116326208147256693337112665176391601562500000 lithium,Li,7.016003442999999784035480843158438801765441894531250000000000,0.924066074714022800407065005856566131114959716796875000000000 beryllium,Be,9.012183159999999304545781342312693595886230468750000000000000,1.000000000000000000000000000000000000000000000000000000000000 boron,B,10.012937300000000817590262158773839473724365234375000000000000,0.199480830670926506664741850727295968681573867797851562500000 boron,B,11.009305299999999405713424494024366140365600585937500000000000,0.800519169329073410068531302385963499546051025390625000000000 carbon,C,12.000000000000000000000000000000000000000000000000000000000000,0.989211941850466902614869013632414862513542175292968750000000 carbon,C,13.003354835199999683936766814440488815307617187500000000000000,0.010788058149533083507343178553128382191061973571777343750000 nitrogen,N,14.003074004200000146624915942084044218063354492187500000000000,0.996358014567941707717579902237048372626304626464843750000000 nitrogen,N,15.000108899400000694868140271864831447601318359375000000000000,0.003641985432058271465738386041266494430601596832275390625000 oxygen,O,15.994914620199999433225457323715090751647949218750000000000000,0.997567609729561044495937949250219389796257019042968750000000 oxygen,O,16.999131757600000725005884305573999881744384765625000000000000,0.000380998476006095935803702490218825005285907536745071411133 oxygen,O,17.999159613700001614233769942075014114379882812500000000000000,0.002051391794432822109073288885383590240962803363800048828125 fluorine,F,18.998403163700000817470936453901231288909912109375000000000000,1.000000000000000000000000000000000000000000000000000000000000 neon,Ne,19.992440181999999282425051205791532993316650390625000000000000,0.904766666333356561757739200402284041047096252441406250000000 neon,Ne,20.993846730000001343796611763536930084228515625000000000000000,0.002709810313278070148523823945652111433446407318115234375000 neon,Ne,21.991385120000000341633494826965034008026123046875000000000000,0.092523523353365264010328417043638182803988456726074218750000 sodium,Na,22.989769282000001027199687086977064609527587890625000000000000,1.000000000000000000000000000000000000000000000000000000000000 magnesium,Mg,23.985041701000000102794729173183441162109375000000000000000000,0.789876809855211581279377242026384919881820678710937500000000 magnesium,Mg,24.985837029999999003848643042147159576416015625000000000000000,0.100001999840012789633192369365133345127105712890625000000000 magnesium,Mg,25.982593019999999484070940525270998477935791015625000000000000,0.110121190304775615209642580794024979695677757263183593750000 aluminium,Al,26.981538579999998717084963573142886161804199218750000000000000,1.000000000000000000000000000000000000000000000000000000000000 silicon,Si,27.976926535299998732853055116720497608184814453125000000000000,0.922220833349999713490774411184247583150863647460937500000000 silicon,Si,28.976494665299998843011053395457565784454345703125000000000000,0.046858437698747611166449900110819726251065731048583984375000 silicon,Si,29.973770011999999240970282698981463909149169921875000000000000,0.030920728951252581667707985957349592354148626327514648437500 phosphorus,P,30.973761998600000566739254281856119632720947265625000000000000,1.000000000000000000000000000000000000000000000000000000000000 sulfur,S,31.972071174100001655915548326447606086730957031250000000000000,0.949850011999040066967836537514813244342803955078125000000000 sulfur,S,32.971458910099997297038498800247907638549804687500000000000000,0.007519398448124149821059081233443066594190895557403564453125 sulfur,S,33.967867030000000738709786674007773399353027343750000000000000,0.042520598352131823427502155254842364229261875152587890625000 sulfur,S,35.967081200000002638716978253796696662902832031250000000000000,0.000109991200703943683199964587160479823069181293249130249023 chlorine,Cl,34.968852730000001827193045755848288536071777343750000000000000,0.757594848103037898923162174469325691461563110351562500000000 chlorine,Cl,36.965902640000003032128006452694535255432128906250000000000000,0.242405151896962045565686594272847287356853485107421875000000 argon,Ar,35.967545119999996927617758046835660934448242187500000000000000,0.003336205796380696270847510120916012965608388185501098632813 argon,Ar,37.962732199999997817485564155504107475280761718750000000000000,0.000629799206452999775149304007015871320618316531181335449219 argon,Ar,39.962383121999998536466591758653521537780761718750000000000000,0.996033994997166272078459314798237755894660949707031250000000 potassium,K,38.963706492999996555681718746200203895568847656250000000000000,0.932580526071084436878777523816097527742385864257812500000000 potassium,K,39.963998240000002226679498562589287757873535156250000000000000,0.000117099885242112454345267402722186034225160256028175354004 potassium,K,40.961825263000001484670065110549330711364746093750000000000000,0.067302374043673424131029037198459263890981674194335937500000 calcium,Ca,39.962590919999996685874066315591335296630859375000000000000000,0.969400838426726974006442105746828019618988037109375000000000 calcium,Ca,41.958618100000002471006155246868729591369628906250000000000000,0.006472228417153705684605746739634923869743943214416503906250 calcium,Ca,42.958766199999999457759258802980184555053710937500000000000000,0.001350985058105257227353823701321289263432845473289489746094 calcium,Ca,43.955482199999998726980265928432345390319824218750000000000000,0.020860869278785776348428271376178599894046783447265625000000 calcium,Ca,45.953691999999996653514244826510548591613769531250000000000000,0.000042999524425259849917842214228613784143817611038684844971 calcium,Ca,47.952522889999997346421878319233655929565429687500000000000000,0.001872079294802999303859447621789513505063951015472412109375 scandium,Sc,44.955908600000000774343789089471101760864257812500000000000000,1.000000000000000000000000000000000000000000000000000000000000 titanium,Ti,45.952628300000000649561116006225347518920898437500000000000000,0.082520097588289403889305617667559999972581863403320312500000 titanium,Ti,46.951759299999999086594471009448170661926269531250000000000000,0.074411070671519405350657905273692449554800987243652343750000 titanium,Ti,47.947942300000001125681592384353280067443847656250000000000000,0.737141543014838140912559083517407998442649841308593750000000 titanium,Ti,48.947866300000001160697138402611017227172851562500000000000000,0.054113506379234489751528514034362160600721836090087890625000 titanium,Ti,49.944787300000001550870365463197231292724609375000000000000000,0.051813782346118462951434224805780104361474514007568359375000 vanadium,V,49.947156700000000739692040951922535896301269531250000000000000,0.002503979968160254584302881752932989911641925573348999023438 vanadium,V,50.943957699999998567363945767283439636230468750000000000000000,0.997496020031839680797247638111002743244171142578125000000000 chromium,Cr,49.946042699999999570081854471936821937561035156250000000000000,0.043450743830478963380947732275672024115920066833496093750000 chromium,Cr,51.940506399999996745009411824867129325866699218750000000000000,0.837881075122238416774678171350387856364250183105468750000000 chromium,Cr,52.940648400000000606269168201833963394165039062500000000000000,0.095010483865806516501351097758742980659008026123046875000000 chromium,Cr,53.938879399999997588111000368371605873107910156250000000000000,0.023657697181476075587447382986283628270030021667480468750000 manganese,Mn,54.938044300000001385342329740524291992187500000000000000000000,1.000000000000000000000000000000000000000000000000000000000000 iron,Fe,53.939609300000000757790985517203807830810546875000000000000000,0.058452792721208068904559240763774141669273376464843750000000 iron,Fe,55.934936299999996833776094717904925346374511718750000000000000,0.917532497856775930422656983864726498723030090332031250000000 iron,Fe,56.935393300000001204352884087711572647094726562500000000000000,0.021190743592002535267138085828264593146741390228271484375000 iron,Fe,57.933274300000000778254616307094693183898925781250000000000000,0.002823965830013456732028309659199294401332736015319824218750 cobalt,Co,58.933194399999997870054357917979359626770019531250000000000000,1.000000000000000000000000000000000000000000000000000000000000 nickel,Ni,57.935342300000002069282345473766326904296875000000000000000000,0.680769095231327558970235713786678388714790344238281250000000 nickel,Ni,59.930786300000001176613295683637261390686035156250000000000000,0.262230419610671172669924544607056304812431335449218750000000 nickel,Ni,60.931056300000001613170752534642815589904785156250000000000000,0.011399083035777891892426083586542517878115177154541015625000 nickel,Ni,61.928345399999997766826709266752004623413085937500000000000000,0.036346250253448952882706635136855766177177429199218750000000 nickel,Ni,63.927967399999999997817212715744972229003906250000000000000000,0.009255151868774300419340228529563319170847535133361816406250 copper,Cu,62.929598400000003266541170887649059295654296875000000000000000,0.691494255172344751692037334578344598412513732910156250000000 copper,Cu,64.927790599999994469726516399532556533813476562500000000000000,0.308505744827655137285660202906001359224319458007812500000000 zinc,Zn,63.929142599999998708426574012264609336853027343750000000000000,0.491645713885820234700929631799226626753807067871093750000000 zinc,Zn,65.926034700000002430897438898682594299316406250000000000000000,0.277325508740183801492662496457342058420181274414062500000000 zinc,Zn,66.927128699999997252234607003629207611083984375000000000000000,0.040405292597461665848879164286699960939586162567138671875000 zinc,Zn,67.924845700000005876972863916307687759399414062500000000000000,0.184515103497573135227227680843498092144727706909179687500000 zinc,Zn,69.925321999999994204699760302901268005371093750000000000000000,0.006108381278961075126765489784474993939511477947235107421875 gallium,Ga,68.925574900000000866384652908891439437866210937500000000000000,0.601079797840404217446064194518839940428733825683593750000000 gallium,Ga,70.924703699999994910285749938338994979858398437500000000000000,0.398920202159595671531633342965506017208099365234375000000000 germanium,Ge,69.924249700000004281719157006591558456420898437500000000000000,0.205705812301332946478993335404084064066410064697265625000000 germanium,Ge,71.922075860000006741756806150078773498535156250000000000000000,0.274503726116209989527305879164487123489379882812500000000000 germanium,Ge,72.923459039999997344239091034978628158569335937500000000000000,0.077504017086240106770844704442424699664115905761718750000000 germanium,Ge,73.921177760999995598467648960649967193603515625000000000000000,0.364982406812098314485837136089685373008251190185546875000000 germanium,Ge,75.921402720000003228051355108618736267089843750000000000000000,0.077304037684118531714716482383664697408676147460937500000000 arsenic,As,74.921595699999997464146872516721487045288085937500000000000000,1.000000000000000000000000000000000000000000000000000000000000 selenium,Se,73.922475910000002841115929186344146728515625000000000000000000,0.008938426836876709608015190156038443092256784439086914062500 selenium,Se,75.919213720000001899279595818370580673217773437500000000000000,0.093712506598838590798905556766840163618326187133789062500000 selenium,Se,76.919914259999998762395989615470170974731445312500000000000000,0.076302570747548426055573145276866853237152099609375000000000 selenium,Se,77.917309200000005375841283239424228668212890625000000000000000,0.237686167234566703143627819372341036796569824218750000000000 selenium,Se,79.916522900000003915010893251746892929077148437500000000000000,0.496053694549759227605534306348999962210655212402343750000000 selenium,Se,81.916700099999999906685843598097562789916992187500000000000000,0.087306634032410290746639702774700708687305450439453125000000 bromine,Br,78.918338099999999712963472120463848114013671875000000000000000,0.506898896176611657438115798868238925933837890625000000000000 bromine,Br,80.916290099999997664781403727829456329345703125000000000000000,0.493101103823388231539581738616107031702995300292968750000000 krypton,Kr,77.920365599999996675251168198883533477783203125000000000000000,0.003552948126957346328819165037771199422422796487808227539063 krypton,Kr,79.916378600000001597436494193971157073974609375000000000000000,0.022860666234272977725971998097520554438233375549316406250000 krypton,Kr,81.913483700000000453655957244336605072021484375000000000000000,0.115931407401451927463575941601447993889451026916503906250000 krypton,Kr,82.914127199999995809776009991765022277832031250000000000000000,0.115000220996773441783922464765055337920784950256347656250000 krypton,Kr,83.911497733000004473069566302001476287841796875000000000000000,0.569863179997571966950431487930472940206527709960937500000000 krypton,Kr,85.910610633000004554560291580855846405029296875000000000000000,0.172791577242972227423933873069472610950469970703125000000000 rubidium,Rb,84.911789742999999930361809674650430679321289062500000000000000,0.721691132354705722207199869444593787193298339843750000000000 rubidium,Rb,86.909180535999993821860698517411947250366210937500000000000000,0.278308867645294166770497668039752170443534851074218750000000 strontium,Sr,83.913419899999993845085555221885442733764648437500000000000000,0.005609775608975640752429381308274969342164695262908935546875 strontium,Sr,85.909261900000004175126377958804368972778320312500000000000000,0.098606055757769678349333730693615507334470748901367187500000 strontium,Sr,86.908878900000004819048626814037561416625976562500000000000000,0.070007199712011511372189431767765199765563011169433593750000 strontium,Sr,87.905613900000005855872586835175752639770507812500000000000000,0.825776968921243081922511919401586055755615234375000000000000 yttrium,Y,88.905842000000006919435691088438034057617187500000000000000000,1.000000000000000000000000000000000000000000000000000000000000 zirconium,Zr,89.904702000000000339241523761302232742309570312500000000000000,0.514422711621750239352479638910153880715370178222656250000000 zirconium,Zr,90.905642000000000280124368146061897277832031250000000000000000,0.112234410554393593262290096390643157064914703369140625000000 zirconium,Zr,91.905032000000005609763320535421371459960937500000000000000000,0.171550886397901253266340404479706194251775741577148437500000 zirconium,Zr,93.906311999999999784449755679816007614135742187500000000000000,0.173788376250214926521664438041625544428825378417968750000000 zirconium,Zr,95.908271999999996637598087545484304428100585937500000000000000,0.028003615175739928616627238966430013533681631088256835937500 niobium,Nb,92.906372000000004618414095602929592132568359375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 molybdenum,Mo,91.906808600000005071706254966557025909423828125000000000000000,0.145308494342837241086741073559096548706293106079101562500000 molybdenum,Mo,93.905085299999996095721144229173660278320312500000000000000000,0.091496458524138415957516201615362660959362983703613281250000 molybdenum,Mo,94.905839299999996683254721574485301971435546875000000000000000,0.158387558641321063435114524509117472916841506958007812500000 molybdenum,Mo,95.904676300000005539914127439260482788085937500000000000000000,0.166690329831184980147185115129104815423488616943359375000000 molybdenum,Mo,96.906018299999999499050318263471126556396484375000000000000000,0.095999792030779435014764544575882609933614730834960937500000 molybdenum,Mo,97.905405299999998192106431815773248672485351562500000000000000,0.243900902666405350327494261364336125552654266357421875000000 molybdenum,Mo,99.907472799999993640085449442267417907714843750000000000000000,0.098216463963333416886669624545902479439973831176757812500000 ruthenium,Ru,95.907590299999995409052644390612840652465820312500000000000000,0.055402974808013198682044020415560225956141948699951171875000 ruthenium,Ru,97.905296000000006984009814914315938949584960937500000000000000,0.018726273471579152340993346115283202379941940307617187500000 ruthenium,Ru,98.905934799999997153463482391089200973510742187500000000000000,0.127588609866636532030881312493875157088041305541992187500000 ruthenium,Ru,99.904214800000005425317795015871524810791015625000000000000000,0.126054915071900669465421174209041055291891098022460937500000 ruthenium,Ru,100.905577899999997271152096800506114959716796875000000000000000,0.170586053375378299268305681835045106709003448486328125000000 ruthenium,Ru,101.904344899999998119710653554648160934448242187500000000000000,0.315451225206183960558803391904802992939949035644531250000000 ruthenium,Ru,103.905432000000004677531251218169927597045898437500000000000000,0.186189948200308125203505937861336860805749893188476562500000 rhodium,Rh,102.905501999999998474777385126799345016479492187500000000000000,1.000000000000000000000000000000000000000000000000000000000000 palladium,Pd,101.905602000000001794433046597987413406372070312500000000000000,0.010207550187954890497099569302008603699505329132080078125000 palladium,Pd,103.904031099999997422855813056230545043945312500000000000000000,0.111463248820283120088525663504697149619460105895996093750000 palladium,Pd,104.905080900000001520311343483626842498779296875000000000000000,0.223336399264176588275176982278935611248016357421875000000000 palladium,Pd,105.903480900000005249239620752632617950439453125000000000000000,0.273264416540030363744762098576757125556468963623046875000000 palladium,Pd,107.903892900000002441629476379603147506713867187500000000000000,0.264546508837878890929573572066146880388259887695312500000000 palladium,Pd,109.905172600000000215914042200893163681030273437500000000000000,0.117181876349676070137029171291942475363612174987792968750000 silver,Ag,106.905091999999996232872945256531238555908203125000000000000000,0.518389668985958174118877650471404194831848144531250000000000 silver,Ag,108.904755100000002698834578040987253189086914062500000000000000,0.481610331014041714858819887012941762804985046386718750000000 cadmium,Cd,105.906460899999999014653440099209547042846679687500000000000000,0.012567197514954164816458614950533956289291381835937500000000 cadmium,Cd,107.904183900000006701702659483999013900756835937500000000000000,0.008928009053980960965657409644791187020018696784973144531250 cadmium,Cd,109.903007400000007010021363385021686553955078125000000000000000,0.124890149496662231087817929164884844794869422912597656250000 cadmium,Cd,110.904183399999993753226590342819690704345703125000000000000000,0.127983459688489453753845737082883715629577636718750000000000 cadmium,Cd,111.902763399999997773193172179162502288818359375000000000000000,0.241267197414976458658131264201074372977018356323242187500000 cadmium,Cd,112.904408300000000053842086344957351684570312500000000000000000,0.122184752800125570604272695618419675156474113464355468750000 cadmium,Cd,113.903365300000004367575456853955984115600585937500000000000000,0.287277937020044504823346187549759633839130401611328125000000 cadmium,Cd,115.904763200000004985668056178838014602661132812500000000000000,0.074901297010766587636254598692175932228565216064453125000000 indium,In,112.904062699999997221311787143349647521972656250000000000000000,0.042954845418549769675564675708301365375518798828125000000000 indium,In,114.903878789000003735054633580148220062255859375000000000000000,0.957045154581450119302132861776044592261314392089843750000000 tin,Sn,111.904824399999995421239873394370079040527343750000000000000000,0.009707379007667929146641050408561568474397063255310058593750 tin,Sn,113.902783700000000521868059877306222915649414062500000000000000,0.006608215781738930282018795736576066701672971248626708984375 tin,Sn,114.903344709999998940475052222609519958496093750000000000000000,0.003409079548521898664348306340343697229400277137756347656250 tin,Sn,115.901743100000004460525815375149250030517578125000000000000000,0.145370749897527656857576516813423950225114822387695312500000 tin,Sn,116.902954300000004650428309105336666107177734375000000000000000,0.076859248003039171148742525474517606198787689208984375000000 tin,Sn,117.901607299999994893369148485362529754638671875000000000000000,0.242144620952342848330118840749491937458515167236328125000000 tin,Sn,118.903311599999994996323948726058006286621093750000000000000000,0.085916802463334898676272644024720648303627967834472656250000 tin,Sn,119.902202700000003687819116748869419097900390625000000000000000,0.325722055045137792728127124064485542476177215576171875000000 tin,Sn,121.903441999999998301973391789942979812622070312500000000000000,0.046317494276545329023875297025369945913553237915039062500000 tin,Sn,123.905277799999993249002727679908275604248046875000000000000000,0.057944355024143474885978122301821713335812091827392578125000 antimony,Sb,120.903812000000002058186510112136602401733398437500000000000000,0.572091349038115315472907695948379114270210266113281250000000 antimony,Sb,122.904212000000001125954440794885158538818359375000000000000000,0.427908650961884573504789841535966843366622924804687500000000 tellurium,Te,119.904061999999996146470948588103055953979492187500000000000000,0.000909764371027903685079651907585684966761618852615356445313 tellurium,Te,121.903041000000001758962753228843212127685546875000000000000000,0.025505394102927340937991829150632838718593120574951171875000 tellurium,Te,122.904270999999994273821357637643814086914062500000000000000000,0.008927687728878220055350745099076448241248726844787597656250 tellurium,Te,123.902821000000002982233127113431692123413085937500000000000000,0.047401722953754971134898710261040832847356796264648437500000 tellurium,Te,124.904431000000002427441359031945466995239257812500000000000000,0.070696689557404629455916733604681212455034255981445312500000 tellurium,Te,125.903311000000002195520210079848766326904296875000000000000000,0.188376210561464557668998054396070074290037155151367187500000 tellurium,Te,127.904461699999998813837009947746992111206054687500000000000000,0.317407791382032011817670991149498149752616882324218750000000 tellurium,Te,129.906222759000002042739652097225189208984375000000000000000000,0.340774739342510235573513455165084451436996459960937500000000 iodine,I,126.904472999999995863618096336722373962402343750000000000000000,1.000000000000000000000000000000000000000000000000000000000000 xenon,Xe,123.905891999999994368408806622028350830078125000000000000000000,0.000952296533640617525774685336870106766582466661930084228516 xenon,Xe,125.904302999999998746716300956904888153076171875000000000000000,0.000890196759683794711613680217254795934422872960567474365234 xenon,Xe,127.903531799999996110273059457540512084960937500000000000000000,0.019102830465697103606848017420816177036613225936889648437500 xenon,Xe,128.904780864000002793545718304812908172607421875000000000000000,0.264005869018636762923790683998959138989448547363281250000000 xenon,Xe,129.903509409999998069906723685562610626220703125000000000000000,0.040709981815666186621971434078659513033926486968994140625000 xenon,Xe,130.905084200000004557296051643788814544677734375000000000000000,0.212323527142361190289676642350968904793262481689453125000000 xenon,Xe,131.904155094000003600740456022322177886962890625000000000000000,0.269085350529324029977829013660084456205368041992187500000000 xenon,Xe,133.905395700000013903263607062399387359619140625000000000000000,0.104356830141138279266499466757522895932197570800781250000000 xenon,Xe,135.907214487999993934863596223294734954833984375000000000000000,0.088573117593851946605099101361702196300029754638671875000000 caesium,Cs,132.905451967000004742658347822725772857666015625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 barium,Ba,129.906321999999988747731549665331840515136718750000000000000000,0.001060985146207953045902061539607075246749445796012878417969 barium,Ba,131.905061799999998584098648279905319213867187500000000000000000,0.001010985846198153050023993415607037604786455631256103515625 barium,Ba,133.904508200000009310315363109111785888671875000000000000000000,0.024171461599537605313692267827718751505017280578613281250000 barium,Ba,134.905688199999985954491421580314636230468750000000000000000000,0.065920277116120362670415033790050074458122253417968750000000 barium,Ba,135.904576200000008157076081261038780212402343750000000000000000,0.078541300421794094099858796198532218113541603088378906250000 barium,Ba,136.905827200000004495450411923229694366455078125000000000000000,0.112320827508414877726750091824214905500411987304687500000000 barium,Ba,137.905247199999990925789461471140384674072265625000000000000000,0.716974162361726841119491382414707913994789123535156250000000 lanthanum,La,137.907123000000012780219549313187599182128906250000000000000000,0.000888171872103250392010975744483403104823082685470581054688 lanthanum,La,138.906362000000001444277586415410041809082031250000000000000000,0.999111828127896672846475212281802669167518615722656250000000 cerium,Ce,135.907129300000008242932381108403205871582031250000000000000000,0.001851973331584025024912354417949700291501358151435852050781 cerium,Ce,137.905998000000010961230145767331123352050781250000000000000000,0.002511963827720880421123794690174690913408994674682617187500 cerium,Ce,139.905441999999993640813045203685760498046875000000000000000000,0.884492463308528265031327464384958148002624511718750000000000 cerium,Ce,141.909252000000009275026968680322170257568359375000000000000000,0.111143599532166723053983048430382041260600090026855468750000 praseodymium,Pr,140.907661999999987756382324732840061187744140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 neodymium,Nd,141.907732000000009975337889045476913452148437500000000000000000,0.271519166958828106483991859931848011910915374755859375000000 neodymium,Nd,142.909821999999991248841979540884494781494140625000000000000000,0.121740433020292235233306143982190405949950218200683593750000 neodymium,Nd,143.910091999999991685399436391890048980712890625000000000000000,0.237977663997580829446931716120161581784486770629882812500000 neodymium,Nd,144.912581999999986237526172772049903869628906250000000000000000,0.082929723850915446070608538775559281930327415466308593750000 neodymium,Nd,145.913121999999987110641086474061012268066406250000000000000000,0.171890140355501652713599014532519504427909851074218750000000 neodymium,Nd,147.916901999999993222445482388138771057128906250000000000000000,0.057561075412857647115583148433870519511401653289794921875000 neodymium,Nd,149.920902000000012321834219619631767272949218750000000000000000,0.056381796404024006608146635244338540360331535339355468750000 samarium,Sm,143.912012000000004263711161911487579345703125000000000000000000,0.030772522277086666181444840617587033193558454513549804687500 samarium,Sm,146.914902000000012094460544176399707794189453125000000000000000,0.149881578776357327065227309503825381398200988769531250000000 samarium,Sm,147.914831999999989875504979863762855529785156250000000000000000,0.112382691006085513873991033051424892619252204895019531250000 samarium,Sm,148.917192000000000007275957614183425903320312500000000000000000,0.138246406123312015612469849656918086111545562744140625000000 samarium,Sm,149.917282000000000152795109897851943969726562500000000000000000,0.073792068527347848272412988990254234522581100463867187500000 samarium,Sm,151.919742000000013604221749119460582733154296875000000000000000,0.267451009404714612482933944193064235150814056396484375000000 samarium,Sm,153.922222000000004982211976312100887298583984375000000000000000,0.227473723885095902019770619517657905817031860351562500000000 europium,Eu,150.919861999999994850440998561680316925048828125000000000000000,0.478103065570820051632949798658955842256546020507812500000000 europium,Eu,152.921242000000006555637810379266738891601562500000000000000000,0.521896934429179837344747738825390115380287170410156250000000 gadolinium,Gd,151.919802000000004227331373840570449829101562500000000000000000,0.002009636255837693018938550082452820788603276014328002929688 gadolinium,Gd,153.920872000000002799424692057073116302490234375000000000000000,0.021826049485043207132317633067941642366349697113037109375000 gadolinium,Gd,154.922631999999993013261700980365276336669921875000000000000000,0.147985214676143617129611129712429828941822052001953125000000 gadolinium,Gd,155.922132000000004836692824028432369232177734375000000000000000,0.204672954195290635048820604424690827727317810058593750000000 gadolinium,Gd,156.923971999999992021912476047873497009277343750000000000000000,0.156491675006823760529783839956508018076419830322265625000000 gadolinium,Gd,157.924112000000008038114174269139766693115234375000000000000000,0.248435033258980114689862261911912355571985244750976562500000 gadolinium,Gd,159.927062000000006491973181255161762237548828125000000000000000,0.218579437121880937322515592313720844686031341552734375000000 terbium,Tb,158.925352000000003727109287865459918975830078125000000000000000,1.000000000000000000000000000000000000000000000000000000000000 dysprosium,Dy,155.924282000000005155015969648957252502441406250000000000000000,0.000562985756460361477619691594753703611786477267742156982422 dysprosium,Dy,157.924421999999992749508237466216087341308593750000000000000000,0.000952975889709990254573812595850768047966994345188140869141 dysprosium,Dy,159.925202000000012958480510860681533813476562500000000000000000,0.023291210732368467645203580218549177516251802444458007812500 dysprosium,Dy,160.926941999999996824044501408934593200683593750000000000000000,0.188889421097646226233024435714469291269779205322265625000000 dysprosium,Dy,161.926812000000012403688742779195308685302734375000000000000000,0.254747154896981076177553404704667627811431884765625000000000 dysprosium,Dy,162.928741999999999734427547082304954528808593750000000000000000,0.248957901365095435330943018925609067082405090332031250000000 dysprosium,Dy,163.929181999999997287886799313127994537353515625000000000000000,0.282598350261738351374418698469526134431362152099609375000000 holmium,Ho,164.930331999999992831362760625779628753662109375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 erbium,Er,161.928791999999987183400662615895271301269531250000000000000000,0.001395973476503946332158423437874716910300776362419128417969 erbium,Er,163.929212000000006810296326875686645507812500000000000000000000,0.016012695758780580435054474719436257146298885345458984375000 erbium,Er,165.930302000000011730662663467228412628173828125000000000000000,0.335027234482544788995994622382568195462226867675781250000000 erbium,Er,166.932051999999998770363163203001022338867187500000000000000000,0.228686654953555862368475004586798604577779769897460937500000 erbium,Er,167.932381999999989830030244775116443634033203125000000000000000,0.269776674243189351631855288360384292900562286376953125000000 erbium,Er,169.935472000000004300090949982404708862304687500000000000000000,0.149100767085425356395234075534972362220287322998046875000000 thulium,Tm,168.934222000000005436959327198565006256103515625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 ytterbium,Yb,167.933891999999985955582815222442150115966796875000000000000000,0.001232929969577727796758992440118163358420133590698242187500 ytterbium,Yb,169.934772000000009484210750088095664978027343750000000000000000,0.029822206098693591902470956256365752778947353363037109375000 ytterbium,Yb,170.936331999999993058736436069011688232421875000000000000000000,0.140905996539396560773838018576498143374919891357421875000000 ytterbium,Yb,171.936392000000012103555491194128990173339843750000000000000000,0.216800685721051017429417129278590437024831771850585937500000 ytterbium,Yb,172.938221999999996114638634026050567626953125000000000000000000,0.161027253651992552363481081556528806686401367187500000000000 ytterbium,Yb,173.938872000000003481545718386769294738769531250000000000000000,0.320249909805123023076589561242144554853439331054687500000000 ytterbium,Yb,175.942581999999987374394549988210201263427734375000000000000000,0.129961018214165419104588750087714288383722305297851562500000 lutetium,Lu,174.940782000000012885720934718847274780273437500000000000000000,0.974008767577204226384424146090168505907058715820312500000000 lutetium,Lu,175.942691999999993868186720646917819976806640625000000000000000,0.025991232422795697287742910930319339968264102935791015625000 hafnium,Hf,173.940052000000008547431207261979579925537109375000000000000000,0.001609652315099938373749166586890169128309935331344604492188 hafnium,Hf,175.941412000000013904355000704526901245117187500000000000000000,0.052668623577307296934613134453684324398636817932128906250000 hafnium,Hf,176.943231999999994741301634348928928375244140625000000000000000,0.185969830516608397585898160286888014525175094604492187500000 hafnium,Hf,177.943712000000004991306923329830169677734375000000000000000000,0.272821070648739838482299546740250661969184875488281250000000 hafnium,Hf,178.945821999999992613084032200276851654052734375000000000000000,0.136190582834107815068946933934057597070932388305664062500000 hafnium,Hf,179.946562000000000125510268844664096832275390625000000000000000,0.350740240108136591690168870627530850470066070556640625000000 tantalum,Ta,179.947462000000001580701791681349277496337890625000000000000000,0.000120131992311552486551486096377772128107608295977115631104 tantalum,Ta,180.948002000000002453816705383360385894775390625000000000000000,0.999879868007688354936135510797612369060516357421875000000000 tungsten,W,179.946711999999990894139045849442481994628906250000000000000000,0.001209872963338849303702171589236513682408258318901062011719 tungsten,W,181.948204699999990907599567435681819915771484375000000000000000,0.264988176241494621798722164385253563523292541503906250000000 tungsten,W,182.950223700000009330324246548116207122802734375000000000000000,0.143124971877952811283307710255030542612075805664062500000000 tungsten,W,183.950931700000012369855539873242378234863281250000000000000000,0.306387829277925793913794905165559612214565277099609375000000 tungsten,W,185.954362000000003263266989961266517639160156250000000000000000,0.284289149639287863635672692907974123954772949218750000000000 rhenium,Re,184.952955900000006295158527791500091552734375000000000000000000,0.374005039798408045470523575204424560070037841796875000000000 rhenium,Re,186.955750999999992245648172684013843536376953125000000000000000,0.625994960201591843507173962279921397566795349121093750000000 osmium,Os,183.952489100000008193092071451246738433837890625000000000000000,0.000209947723016968765524098428087995671376120299100875854492 osmium,Os,185.953841000000011263182386755943298339843750000000000000000000,0.015926034417430057904541129687459033448249101638793945312500 osmium,Os,186.955750999999992245648172684013843536376953125000000000000000,0.019615115836156795520173190539026109036058187484741210937500 osmium,Os,187.955840999999992391167324967682361602783203125000000000000000,0.132457018202467580181291850749403238296508789062500000000000 osmium,Os,188.958142000000009375071385875344276428222656250000000000000000,0.161519781574387955025429164379602298140525817871093750000000 osmium,Os,189.958441999999990912328939884901046752929687500000000000000000,0.262554623898649197588639481182326562702655792236328125000000 osmium,Os,191.961481999999989511707099154591560363769531250000000000000000,0.407717478347891348899878494194126687943935394287109375000000 iridium,Ir,190.960591999999991230652085505425930023193359375000000000000000,0.373050779688124722888176165724871680140495300292968750000000 iridium,Ir,192.962921999999991840013535693287849426269531250000000000000000,0.626949220311875166089521371759474277496337890625000000000000 platinum,Pt,189.959934000000004061803338117897510528564453125000000000000000,0.000121987349911814132899338936066868654961581341922283172607 platinum,Pt,191.961041999999991958247846923768520355224609375000000000000000,0.007821588901230941415221309398475568741559982299804687500000 platinum,Pt,193.962681699999990314609021879732608795166015625000000000000000,0.328605923565726210089366077227168716490268707275390625000000 platinum,Pt,194.964792700000003833338269032537937164306640625000000000000000,0.337788971283677852408544595164130441844463348388671875000000 platinum,Pt,195.964952699999997776103555224835872650146484375000000000000000,0.252107856415289710572125159160350449383258819580078125000000 platinum,Pt,197.967892000000006191839929670095443725585937500000000000000000,0.073553672484163390432598816914833150804042816162109375000000 gold,Au,196.966569600000013906537787988781929016113281250000000000000000,1.000000000000000000000000000000000000000000000000000000000000 mercury,Hg,195.965832000000006019035936333239078521728515625000000000000000,0.001509815802472098391837085351596670079743489623069763183594 mercury,Hg,197.966769300000009934592526406049728393554687500000000000000000,0.099707835644051417967048678292485419660806655883789062500000 mercury,Hg,198.968281300000001010630512610077857971191406250000000000000000,0.168701418426951910145561441822792403399944305419921875000000 mercury,Hg,199.968327299999998558632796630263328552246093750000000000000000,0.230990819120067331082779560347262304276227951049804687500000 mercury,Hg,200.970303599999994048630469478666782379150390625000000000000000,0.131793921141620695713925215386552736163139343261718750000000 mercury,Hg,201.970643599999988282434060238301753997802734375000000000000000,0.298589572072207154462830658303573727607727050781250000000000 mercury,Hg,203.973494299999998702332959510385990142822265625000000000000000,0.068706617792629293139938795320631470531225204467773437500000 thallium,Tl,202.972345100000012507734936662018299102783203125000000000000000,0.295204095918081610427918803907232359051704406738281250000000 thallium,Tl,204.974428100000011454540072008967399597167968750000000000000000,0.704795904081918278549778733577113598585128784179687500000000 lead,Pb,203.973044899999990775540936738252639770507812500000000000000000,0.014094362255097959285565778486670751590281724929809570312500 lead,Pb,205.974466900000010127769201062619686126708984375000000000000000,0.241003598560575765796798464180028531700372695922851562500000 lead,Pb,206.975897900000006757181836292147636413574218750000000000000000,0.221011595361855245345239495691203046590089797973632812500000 lead,Pb,207.976653900000002295200829394161701202392578125000000000000000,0.523890443822470963652904174523428082466125488281250000000000 bismuth,Bi,208.980401000000000522049958817660808563232421875000000000000000,1.000000000000000000000000000000000000000000000000000000000000 uranium,U,234.040952000000004318280844017863273620605468750000000000000000,0.000054599923560107009460132254652364736102754250168800354004 uranium,U,235.043932000000012294549378566443920135498046875000000000000000,0.007204689913434121108226637630878030904568731784820556640625 uranium,U,238.050792000000001280568540096282958984375000000000000000000000,0.992740710163005690702675565262325108051300048828125000000000 thorium,Th,232.038061999999996487531461752951145172119140625000000000000000,1.000000000000000000000000000000000000000000000000000000000000 protactinium,Pa,231.035881999999986646798788569867610931396484375000000000000000,1.000000000000000000000000000000000000000000000000000000000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/leucine.svg000664 001750 001750 00000004125 15100504560 035010 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/lysine.svg000664 001750 001750 00000004454 15100504560 034674 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/methionine.svg000664 001750 001750 00000004340 15100504560 035443 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/modification_dictionary000664 001750 001750 00000003066 15100504560 037404 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the modification_dictionary file where the correspondences # between the name of each modification and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. Also, the graphical operation that is to # be performed upon chemical modification of a monomer is listed ('T' # for transparent and 'O' for opaque). See the manual for details. # The format of the file is like this : # ------------------------------------- # Phosphorylation%T%phospho.svg # where Phosphorylation is the name of the modification. T indicates # that the visual rendering of the modification is a transparent # process (O indicates that the visual rendering of the modification # is a full image replacement 'O' like opaque). phospho.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Phosphorylation%T%phospho.svg Sulphation%T%sulpho.svg AmidationAsp%O%asparagine.svg Acetylation%T%acetyl.svg AmidationGlu%O%glutamine.svg Oxidation%T%oxidation.svg SulfideBond%T%sulfbond.svg ProtonLoss%T%protonloss.svg tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/monomer_dictionary000664 001750 001750 00000002426 15100504560 036412 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# This file is part of the massXpert project. # The "massXpert" project is released ---in its entirety--- under the # GNU General Public License and was started (in the form of the GNU # polyxmass project) at the Centre National de la Recherche # Scientifique (FRANCE), that granted me the formal authorization to # publish it under this Free Software License. # Copyright (C) 2006,2007 Filippo Rusconi # This is the monomer_dictionary file where the correspondences # between the codes of each monomer and their graphic file (pixmap # file called "image") used to graphicallly render them in the # sequence editor are made. # The format of the file is like this : # ------------------------------------- # Ala%alanine.svg # where Ala is the monomer code and alanine.svg is a # resolution-independent svg file. # Each line starting with a '#' character is a comment and is ignored # during parsing of this file. # This file is case-sensitive. Ala%alanine.svg Cys%cysteine.svg Asp%aspartate.svg Glu%glutamate.svg Phe%phenylalanine.svg Gly%glycine.svg His%histidine.svg Ile%isoleucine.svg Lys%lysine.svg Leu%leucine.svg Met%methionine.svg Asn%asparagine.svg Pro%proline.svg Gln%glutamine.svg Arg%arginine.svg Ser%serine.svg Thr%threonine.svg Val%valine.svg Trp%tryptophan.svg Tyr%tyrosine.svg tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/oxidation.svg000664 001750 001750 00000005727 15100504560 035314 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/pdb-code-conversion.dic000664 001750 001750 00000000557 15100504560 037112 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0# Converts from code on the left of '>' to code on the right. # Number of letters allowed in each code is # described with syntax 1>3 and that line should be the first # non-comment line in this file. 3>3 ALA>Ala CYS>Cys ASP>Asp GLU>Glu PHE>Phe GLY>Gly HIS>His ILE>Ile LYS>Lys LEU>Leu MET>Met ASN>Asn PRO>Pro GLN>Gln ARG>Arg SER>Ser THR>Thr VAL>Val TRP>Trp TYR>Tyr tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/phenylalanine.svg000664 001750 001750 00000004672 15100504560 036143 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/phospho.svg000664 001750 001750 00000002365 15100504560 035050 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/proline.svg000664 001750 001750 00000004064 15100504560 035036 0ustar00rusconirusconi000000 000000 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/protein-3-letters.xml000664 001750 001750 00000026113 15100504560 036607 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> protein-3-letters "N-term amine" +H "C-term carboxylic acid" +OH 3 +H 1 1 Glycine Gly C2H3NO Alanine Ala C3H5NO Valine Val C5H9NO Leucine Leu C6H11NO DiaminoPimelic Dap C7H12N2O3 Isoleucine Ile C6H11NO Serine Ser C3H5NO2 Threonine Thr C4H7NO2 Cysteine Cys C3H5NOS Methionine Met C5H9NOS Arginine Arg C6H12N4O Lysine Lys C6H12N2O Aspartate Asp C4H5NO3 Glutamate Glu C5H7NO3 Asparagine Asn C4H6N2O2 Glutamine Gln C5H8N2O2 Tryptophan Trp C11H10N2O Phenylalanine Phe C9H9N1O Tyrosine Tyr C9H9N1O2 Histidine His C6H7N3O Proline Pro C5H7N1O1 Acetylation C2H2O1 ;Lys; 1 AmidationAsp H1N1-O1 ;Asp; 1 AmidationGlu H1N1-O1 ;Glu; 1 Carbamidomethylation C2H3N1O1 ;Cys; 1 Carbamylation C1H1N1O1 ;Lys; 1 CarboxyMethylation C2H2O2 ;Cys; 1 Chromo-O -O1 ;Thr;Ser;Gly; 1 Chromo-H -H1 ;Gly; 1 Chromo-H3 -H3 ;Tyr;Phe;Trp; 1 Dehydroalanine -H2S1 ;Cys; 1 DTNB (adsorbed) C14H8N2O8S2 ;Cys; 1 Dehydroxylation -H1O1 ;Asp;Glu; 1 Formylation C1O1 * 1 GlcNAcMurNAc(R) C19H32N2O12 * 1 Glutamylation C5H7N1O3 ;Glu; 6 Glycylation C2H3N1O1 ;Glu; 34 Hydroxylation H1O1 ;Phe; 1 LysMethylation C1H2 ;Lys; 3 Methylation C1H2 ;Lys;Arg; 2 Oxidation O1 ;Met;Tyr; 1 Phosphorylation H1O3P1 ;Ser;Thr;Tyr; 1 ProtonLoss -H1 ;Cys;Tyr;Lys; 1 SulfideBond -H2 ;Cys; 1 Sulphation O3S1 ;Ser;Thr;Tyr; 1 SPITC C7H5N1O3S2 * 1 TNB C7H3N1O4S1 ;Cys; 1 CFP-chromophore Chromo-O Chromo-H3 Chromo-H DisulfideBond ProtonLoss ProtonLoss EndoAspN+GluN /Asp;/Glu Trypsin Lys/;Arg/;-Lys/Pro Chymotrypsin Trp/;Val/ EndoLysC Lys/ EndoAspN /Asp GluC Glu/ CyanogenBromide Met/ Homoseryl Met -CH2S+O EndoAspN-Trypsin /Asp;Lys/;Arg/;-Lys/Pro a LE -C1O1H1 0 b LE -H 0 c LE +N1H2 0 that's just a comment z RE -N1H1 0 Typically in ECD of protonated ions y RE +H1 0 x RE +C1O1-H1 0 imm NE -C1O1 0 tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/protonloss.svg000664 001750 001750 00000004266 15100504560 035535 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/serine.svg000664 001750 001750 00000005230 15100504560 034647 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/sulfbond.svg000664 001750 001750 00000011620 15100504560 035176 0ustar00rusconirusconi000000 000000 image/svg+xml libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/sulpho.svg000664 001750 001750 00000006334 15100504560 034702 0ustar00rusconirusconi000000 000000 image/svg+xml tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/threonine.svg000664 001750 001750 00000003560 15100504560 035302 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/tryptophan.svg000664 001750 001750 00000003744 15100504560 035523 0ustar00rusconirusconi000000 000000 libxpertmass-1.4.0 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/tyrosine.svg000664 001750 001750 00000003520 15100504560 035236 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-chemistry-definitions/version-2/protein-3-letters/valine.svg000664 001750 001750 00000004147 15100504560 034646 0ustar00rusconirusconi000000 000000 ]> libxpertmass-1.4.0/tests/data/polymer-sequences/000775 001750 001750 00000000000 15100507137 023173 5ustar00rusconirusconi000000 000000 libxpertmass-1.4.0/tests/data/polymer-sequences/chicken-telokin.mxp000664 001750 001750 00000003566 15100504560 026777 0ustar00rusconirusconi000000 000000 ]> protein-1-letter chicken telokin NOT_SET rusconi 2024-10-15:36:36 MAMISGMSGRKA S Phosphorylation H1O3P1 * 1 GSSPTSPINADKVENEDAFLEEVAEEKPHVKPYFTKTILDMEVVEGSAARFDCKIEGYPDPEVMWYKDDQPVKESRHFQIDYDEEGNCSLTISEVCGDDDAKYTCKAVNSLGEATCTAELLVETMGKEGEGEGEGEEDEEEEEE Acetylation C2H2O1 * 1 AmidationGlu H1N1-O1 * 1 libxpertmass-1.4.0/tests/data/polymer-sequences/cyan-fluorescent-protein-cross-link.mxp000664 001750 001750 00000003135 15100504560 032741 0ustar00rusconirusconi000000 000000 ]> protein-1-letter CFP TEV-cut NOT_SET rusconi 2008-03-18:01:38 GAVSKGEELFTGVVPILVELDGDVNGHKFSVSGEGEGDATYGKLTLKFICTTGKLPVPWPTLVTTLTWGVQCFSRYPDHMKQHDFFKSAMPEGYVQERTIFFKDDGNYKTRAEVKFEGDTLVNRIELKGIDFKEDGNILGHKLEYNYISHNVYITADKQKNGIKANFKIRHNIEDGSVQLADHYQQNTPIGDGPVLLPDNHYLSTQSALSKDPNEKRDHMVLLEFVTAAGITLGMDELYK CFP-chromophore ;66;67;68; libxpertmass-1.4.0/tests/data/polymer-sequences/cyan-fluorescent-protein-no-cross-link.mxp000664 001750 001750 00000002725 15100504560 033357 0ustar00rusconirusconi000000 000000 ]> protein-1-letter CFP TEV-cut NOT_SET rusconi 2008-03-18:01:38 GAVSKGEELFTGVVPILVELDGDVNGHKFSVSGEGEGDATYGKLTLKFICTTGKLPVPWPTLVTTLTWGVQCFSRYPDHMKQHDFFKSAMPEGYVQERTIFFKDDGNYKTRAEVKFEGDTLVNRIELKGIDFKEDGNILGHKLEYNYISHNVYITADKQKNGIKANFKIRHNIEDGSVQLADHYQQNTPIGDGPVLLPDNHYLSTQSALSKDPNEKRDHMVLLEFVTAAGITLGMDELYK libxpertmass-1.4.0/tests/data/polymer-sequences/horse-myoglobin.mxp000664 001750 001750 00000002607 15100504560 027040 0ustar00rusconirusconi000000 000000 ]> protein-1-letter Not Set NOT_SET rusconi 2008-12-11:56:11 GLSDGEWQQVLNVWGKVEADIAGHGQEVLIRLFTGHPETLEKFDKFKHLKTEAEMKASEDLKKHGTVVLTALGGILKKKGHHEAELKPLAQSHATKHKIPIKYLEFISDAIIHVLHSKHPGDFGADAQGAMTKALELFRNDIAAKYKELGFQG libxpertmass-1.4.0/tests/data/polymer-sequences/kunitz-inhibitor-human-cross-links.mxp000664 001750 001750 00000004767 15100504560 032620 0ustar00rusconirusconi000000 000000 ]> protein-1-letter NOT_SET NOT_SET rusconi 2024-10-17:13:51 MRSLGALLLLLSACLAVSAGPVPTPPDNIQVQENFNISRIYGKWYNLAIGSTCPWLKKIMDRMTVSTLVLGEGATEAEISMTSTRWRKGVCEETSGAYEKTDTDGKFLYHKSKWNITMESYVVHTNYDEYAIFLTKKFSRHHGPTITAKLYGRAPQLRETLLQDFRVVAQGVGIPEDSIFTMADRGECVPGEQEPEPILIPRVRRAVLPQEEEGSGGGQLVTEVTKKEDSCQLGYSAGPCMGMTSRYFYNGTSMACETFQYGGCMGNGNNFVTEKECLQTCRTVAACNLPIVRGPCRAFIQLWAFDAVKGKCVLFPYGGCQGNGNKFYSEKECREYCGVPGDGDEELLRFSN DisulfideBond ;90;187; NOT_SET DisulfideBond ;230;280; NOT_SET DisulfideBond ;239;263; NOT_SET DisulfideBond ;255;276; NOT_SET DisulfideBond ;286;336; NOT_SET DisulfideBond ;295;319; NOT_SET DisulfideBond ;311;332; NOT_SET libxpertmass-1.4.0/tests/data/polymer-sequences/kunitz-inhibitor-human-no-cross-link.mxp000664 001750 001750 00000003116 15100504560 033032 0ustar00rusconirusconi000000 000000 ]> protein-1-letter NOT_SET NOT_SET rusconi 2024-10-17:11:53 MRSLGALLLLLSACLAVSAGPVPTPPDNIQVQENFNISRIYGKWYNLAIGSTCPWLKKIMDRMTVSTLVLGEGATEAEISMTSTRWRKGVCEETSGAYEKTDTDGKFLYHKSKWNITMESYVVHTNYDEYAIFLTKKFSRHHGPTITAKLYGRAPQLRETLLQDFRVVAQGVGIPEDSIFTMADRGECVPGEQEPEPILIPRVRRAVLPQEEEGSGGGQLVTEVTKKEDSCQLGYSAGPCMGMTSRYFYNGTSMACETFQYGGCMGNGNNFVTEKECLQTCRTVAACNLPIVRGPCRAFIQLWAFDAVKGKCVLFPYGGCQGNGNKFYSEKECREYCGVPGDGDEELLRFSN libxpertmass-1.4.0/tests/data/polymer-sequences/kunitz-inhibitor-human.mxp000664 001750 001750 00000004767 15100504560 030353 0ustar00rusconirusconi000000 000000 ]> protein-1-letter NOT_SET NOT_SET rusconi 2009-08-21:37:04 MRSLGALLLLLSACLAVSAGPVPTPPDNIQVQENFNISRIYGKWYNLAIGSTCPWLKKIMDRMTVSTLVLGEGATEAEISMTSTRWRKGVCEETSGAYEKTDTDGKFLYHKSKWNITMESYVVHTNYDEYAIFLTKKFSRHHGPTITAKLYGRAPQLRETLLQDFRVVAQGVGIPEDSIFTMADRGECVPGEQEPEPILIPRVRRAVLPQEEEGSGGGQLVTEVTKKEDSCQLGYSAGPCMGMTSRYFYNGTSMACETFQYGGCMGNGNNFVTEKECLQTCRTVAACNLPIVRGPCRAFIQLWAFDAVKGKCVLFPYGGCQGNGNKFYSEKECREYCGVPGDGDEELLRFSN DisulfideBond ;90;187; NOT_SET DisulfideBond ;230;280; NOT_SET DisulfideBond ;239;263; NOT_SET DisulfideBond ;255;276; NOT_SET DisulfideBond ;286;336; NOT_SET DisulfideBond ;295;319; NOT_SET DisulfideBond ;311;332; NOT_SET libxpertmass-1.4.0/tests/test_CalcOptions.cpp000664 001750 001750 00000024620 15100504560 022564 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("CalcOptions object can be constructed empty", "[CalcOptions]") { WHEN("Constructing an empty CalcOptions") { CalcOptions calc_options; THEN("The object is configured with default values") { REQUIRE_FALSE(calc_options.isDeepCalculation()); REQUIRE(calc_options.getMassType() == Enums::MassType::BOTH); REQUIRE(calc_options.getCapType() == Enums::CapType::BOTH); REQUIRE(calc_options.getMonomerEntities() == Enums::ChemicalEntity::NONE); REQUIRE(calc_options.getPolymerEntities() == Enums::ChemicalEntity::NONE); REQUIRE(calc_options.getSelectionType() == Enums::SelectionType::OLIGOMERS); REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 0); REQUIRE(calc_options.getIndexRangeCollectionRef().size() == 0); } AND_WHEN("Manually setting the members to other values") { calc_options.setDeepCalculation(true); calc_options.setMassType(Enums::MassType::MONO); calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS); calc_options.setCapType(Enums::CapType::LEFT); calc_options.setMonomerEntities(Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); THEN("All the values are properly set.") { REQUIRE(calc_options.isDeepCalculation()); REQUIRE(calc_options.getMassType() == Enums::MassType::MONO); REQUIRE(calc_options.getCapType() == Enums::CapType::LEFT); REQUIRE(calc_options.getMonomerEntities() == Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); REQUIRE(calc_options.getPolymerEntities() == Enums::ChemicalEntity::LEFT_END_MODIF); REQUIRE(calc_options.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS); REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 0); REQUIRE(calc_options.getIndexRangeCollectionRef().size() == 0); } } } } SCENARIO("CalcOptions object can be set with IndexRange instances", "[CalcOptions]") { WHEN("Constructing empty CalcOptions and configuring IndexRange instances") { CalcOptions calc_options; CalcOptions calc_options_0; CalcOptions calc_options_1; CalcOptions calc_options_2; std::size_t index_range_start = 200; std::size_t index_range_stop = 250; IndexRange index_range_0(0, 50); IndexRange index_range_1(51, 100); IndexRange index_range_2(101, 150); IndexRangeCollection index_range_collection; index_range_collection.appendIndexRange(index_range_0); index_range_collection.appendIndexRange(index_range_1); index_range_collection.appendIndexRange(index_range_2); calc_options.setIndexRange(index_range_start, index_range_stop); calc_options_0.setIndexRange(index_range_0); calc_options_1.setIndexRanges(index_range_collection); calc_options_2.setIndexRanges(index_range_collection); calc_options_2.setIndexRange(index_range_start, index_range_stop); THEN( "The member IndexRangeCollection must contain the right IndexRange " "instances") { REQUIRE(calc_options.getIndexRangeCollectionCstRef().size() == 1); REQUIRE(calc_options.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() .start == index_range_start); REQUIRE(calc_options.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() .stop == index_range_stop); REQUIRE(calc_options_0.getIndexRangeCollectionCstRef().size() == 1); REQUIRE(calc_options_0.getIndexRangeCollectionCstRef().getRangeCstRefAt( 0) == index_range_0); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().size() == 3); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 0) == index_range_0); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 1) == index_range_1); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 2) == index_range_2); REQUIRE(calc_options_2.getIndexRangeCollectionCstRef().size() == 1); REQUIRE(calc_options_2.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() .start == index_range_start); REQUIRE(calc_options_2.getIndexRangeCollectionCstRef() .getRangesCstRef() .front() .stop == index_range_stop); } } } SCENARIO("CalcOptions objects can be assigned using operator=()", "[CalcOptions]") { WHEN("Construct a CalcOptions instance and configure it fully") { CalcOptions calc_options; calc_options.setDeepCalculation(true); calc_options.setMassType(Enums::MassType::MONO); calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS); calc_options.setCapType(Enums::CapType::LEFT); calc_options.setMonomerEntities(Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); IndexRange index_range_0(0, 50); IndexRange index_range_1(51, 100); IndexRange index_range_2(101, 150); IndexRangeCollection index_range_collection; index_range_collection.appendIndexRange(index_range_0); index_range_collection.appendIndexRange(index_range_1); index_range_collection.appendIndexRange(index_range_2); calc_options.setIndexRanges(index_range_collection); AND_WHEN("This CalcOptions instance is assigned to another") { CalcOptions calc_options_1; calc_options_1 = calc_options; THEN("The new instance must be identical to the first") { REQUIRE(calc_options_1.isDeepCalculation()); REQUIRE(calc_options_1.getMassType() == Enums::MassType::MONO); REQUIRE(calc_options_1.getCapType() == Enums::CapType::LEFT); REQUIRE(calc_options_1.getMonomerEntities() == Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); REQUIRE(calc_options_1.getPolymerEntities() == Enums::ChemicalEntity::LEFT_END_MODIF); REQUIRE(calc_options_1.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().size() == 3); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 0) == index_range_0); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 1) == index_range_1); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 2) == index_range_2); } } } } SCENARIO("CalcOptions objects can be copied with the copy constructor", "[CalcOptions]") { WHEN("Construct a CalcOptions instance and configure it fully") { CalcOptions calc_options; calc_options.setDeepCalculation(true); calc_options.setMassType(Enums::MassType::MONO); calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS); calc_options.setCapType(Enums::CapType::LEFT); calc_options.setMonomerEntities(Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); IndexRange index_range_0(0, 50); IndexRange index_range_1(51, 100); IndexRange index_range_2(101, 150); IndexRangeCollection index_range_collection; index_range_collection.appendIndexRange(index_range_0); index_range_collection.appendIndexRange(index_range_1); index_range_collection.appendIndexRange(index_range_2); calc_options.setIndexRanges(index_range_collection); AND_WHEN("This CalcOptions instance is used with the copy constructor") { CalcOptions calc_options_1(calc_options); THEN("The new instance must be identical to the first") { REQUIRE(calc_options_1.isDeepCalculation()); REQUIRE(calc_options_1.getMassType() == Enums::MassType::MONO); REQUIRE(calc_options_1.getCapType() == Enums::CapType::LEFT); REQUIRE(calc_options_1.getMonomerEntities() == Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); REQUIRE(calc_options_1.getPolymerEntities() == Enums::ChemicalEntity::LEFT_END_MODIF); REQUIRE(calc_options_1.getSelectionType() == Enums::SelectionType::RESIDUAL_CHAINS); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().size() == 3); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 0) == index_range_0); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 1) == index_range_1); REQUIRE(calc_options_1.getIndexRangeCollectionCstRef().getRangeCstRefAt( 2) == index_range_2); } } } } SCENARIO("CalcOptions objects can be documented as strings", "[CalcOptions]") { WHEN("Construct a CalcOptions instance and configure it fully") { CalcOptions calc_options; calc_options.setDeepCalculation(true); calc_options.setMassType(Enums::MassType::MONO); calc_options.setSelectionType(Enums::SelectionType::RESIDUAL_CHAINS); calc_options.setCapType(Enums::CapType::LEFT); calc_options.setMonomerEntities(Enums::ChemicalEntity::MODIF_AND_CROSS_LINKER); calc_options.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); IndexRange index_range_0(0, 50); IndexRange index_range_1(51, 100); IndexRange index_range_2(101, 150); IndexRangeCollection index_range_collection; index_range_collection.appendIndexRange(index_range_0); index_range_collection.appendIndexRange(index_range_1); index_range_collection.appendIndexRange(index_range_2); calc_options.setIndexRanges(index_range_collection); AND_WHEN("This CalcOptions instance outputs itself as a string") { QString calc_options_text = calc_options.toString(); qDebug().noquote() << "The text:" << calc_options_text; } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_ChemicalGroup.cpp000664 001750 001750 00000035122 15100504560 023067 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_chemical_group_rule_1_letter("protein-1-letter", 1); ErrorList error_list_chemical_group_rule; SCENARIO("ChemicalGroup object can be constructed empty", "[ChemicalGroup]") { WHEN("Constructing an empty ChemicalGroup") { ChemicalGroup chemical_group; THEN("The object is invalid") { REQUIRE_FALSE(chemical_group.isValid()); } AND_WHEN( "Setting the name, the pKa, the charge bool, the Enums::ChemicalGroupTrapped") { chemical_group.setName("N-term NH2"); chemical_group.setPka(9.6); chemical_group.setAcidCharged(true); chemical_group.setPolRule(Enums::ChemicalGroupTrapped::LEFT); THEN("The ChemicalGroup is valid.") { REQUIRE(chemical_group.isValid()); } AND_WHEN("Constructing two fully initialized ChemicalGroupRule") { ChemicalGroupRule chemical_group_rule_1; chemical_group_rule_1.setName("Acetylation"); chemical_group_rule_1.setEntity("LE_PLM_MODIF"); chemical_group_rule_1.setFate(Enums::ChemicalGroupFate::LOST); ChemicalGroupRule chemical_group_rule_2; chemical_group_rule_2.setName("Formylation"); chemical_group_rule_2.setEntity("LE_PLM_MODIF"); chemical_group_rule_2.setFate(Enums::ChemicalGroupFate::LOST); AND_WHEN("Adding that rule to the chemical group") { chemical_group.getRulesRef().push_back( std::make_shared(chemical_group_rule_1)); chemical_group.getRulesRef().push_back( std::make_shared(chemical_group_rule_2)); REQUIRE(chemical_group.getRulesRef().size() == 2); std::size_t index = 0; THEN( "The ChemicalGroupRule instances are findable in the " "ChemicalGroup.") { REQUIRE(chemical_group.getRulesRef().size() == 2); REQUIRE(chemical_group.getRulesCstRef().size() == 2); ChemicalGroupRuleSPtr chemical_group_rule_1_sp = chemical_group.getRulesCstRef().at(0); REQUIRE(chemical_group_rule_1_sp != nullptr); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); // Begin erroneous cases REQUIRE(nullptr == chemical_group.findRuleByEntity("Not_existent", index)); std::size_t out_of_bounds_index = 10; REQUIRE(nullptr == chemical_group.findRuleByEntity( "LE_PLM_MODIF", out_of_bounds_index)); REQUIRE(nullptr == chemical_group.findRuleByName("Not_existent", index)); REQUIRE(nullptr == chemical_group.findRuleByName( "Acetylation", out_of_bounds_index)); // End erroneous cases chemical_group_rule_1_sp = chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 0); chemical_group_rule_1_sp = chemical_group.findRuleByName("Acetylation", index); REQUIRE(chemical_group_rule_1_sp->getName().toStdString() == "Acetylation"); REQUIRE(index == 0); chemical_group_rule_1_sp = chemical_group.findRuleByEntityAndName( "LE_PLM_MODIF", "Acetylation", index); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(chemical_group_rule_1_sp->getName().toStdString() == "Acetylation"); REQUIRE(index == 0); ChemicalGroupRuleSPtr chemical_group_rule_2_sp = chemical_group.getRulesRef().at(1); REQUIRE(chemical_group_rule_2_sp != nullptr); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); index = 0; chemical_group_rule_2_sp = chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 0); index = 1; chemical_group_rule_2_sp = chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 1); index = 0; chemical_group_rule_2_sp = chemical_group.findRuleByName("Formylation", index); REQUIRE(chemical_group_rule_2_sp->getName().toStdString() == "Formylation"); REQUIRE(index == 1); index = 0; chemical_group_rule_2_sp = chemical_group.findRuleByEntityAndName( "LE_PLM_MODIF", "Formylation", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(chemical_group_rule_2_sp->getName().toStdString() == "Formylation"); REQUIRE(index == 1); } AND_WHEN( "An new ChemicalGroup is allocated and initialized with " "operator=() or allocated with the copy constructor") { ChemicalGroup other_chemical_group; other_chemical_group = chemical_group; ChemicalGroup another_chemical_group(other_chemical_group); THEN("The new ChemicalGroup is identical to the first one.") { REQUIRE(another_chemical_group.isValid()); REQUIRE(another_chemical_group.getName().toStdString() == "N-term NH2"); REQUIRE_THAT(chemical_group.getPka(), Catch::Matchers::WithinAbs(9.6, 0.0001)); REQUIRE(chemical_group.isAcidCharged()); REQUIRE(chemical_group.getPolRule() == Enums::ChemicalGroupTrapped::LEFT); ChemicalGroupRuleSPtr chemical_group_rule_1_sp = another_chemical_group.getRulesRef().at(0); REQUIRE(chemical_group_rule_1_sp != nullptr); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); chemical_group_rule_1_sp = another_chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 0); chemical_group_rule_1_sp = another_chemical_group.findRuleByName("Acetylation", index); REQUIRE(chemical_group_rule_1_sp->getName().toStdString() == "Acetylation"); REQUIRE(index == 0); chemical_group_rule_1_sp = another_chemical_group.findRuleByEntityAndName( "LE_PLM_MODIF", "Acetylation", index); REQUIRE(chemical_group_rule_1_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(chemical_group_rule_1_sp->getName().toStdString() == "Acetylation"); REQUIRE(index == 0); ChemicalGroupRuleSPtr chemical_group_rule_2_sp = another_chemical_group.getRulesRef().at(1); REQUIRE(chemical_group_rule_2_sp != nullptr); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); index = 0; chemical_group_rule_2_sp = another_chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 0); index = 1; chemical_group_rule_2_sp = another_chemical_group.findRuleByEntity("LE_PLM_MODIF", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(index == 1); index = 0; chemical_group_rule_2_sp = another_chemical_group.findRuleByName("Formylation", index); REQUIRE(chemical_group_rule_2_sp->getName().toStdString() == "Formylation"); REQUIRE(index == 1); index = 0; chemical_group_rule_2_sp = another_chemical_group.findRuleByEntityAndName( "LE_PLM_MODIF", "Formylation", index); REQUIRE(chemical_group_rule_2_sp->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(chemical_group_rule_2_sp->getName().toStdString() == "Formylation"); REQUIRE(index == 1); } } } } } } } SCENARIO( "ChemicalGroup objects can be created by reading a mnmchemgroup XML element", "[ChemicalGroup]") { WHEN("Constructing an empty ChemicalGroup") { ChemicalGroup chemical_group; AND_WHEN("Initializing it with a XML element") { // QString dom_string = "N-term // NH29.6TRUEleft_trappedLE_PLM_MODIFAcetylationLOST"; QDomDocument document; QDomElement mnmchemgroup_element = document.createElement("mnmchemgroup"); QDomElement name_element = document.createElement("name"); QDomText name_text = document.createTextNode("N-term NH2"); name_element.appendChild(name_text); QDomElement pka_element = document.createElement("pka"); QDomText pka_text = document.createTextNode("9.6"); pka_element.appendChild(pka_text); QDomElement acidcharged_element = document.createElement("acidcharged"); QDomText acidcharged_text = document.createTextNode("TRUE"); acidcharged_element.appendChild(acidcharged_text); QDomElement polrule_element = document.createElement("polrule"); QDomText polrule_text = document.createTextNode("left_trapped"); polrule_element.appendChild(polrule_text); QDomElement chemgrouprule_element = document.createElement("chemgrouprule"); QDomElement entity_element = document.createElement("entity"); QDomText entity_text = document.createTextNode("LE_PLM_MODIF"); entity_element.appendChild(entity_text); QDomElement rule_name_element = document.createElement("name"); QDomText rule_name_text = document.createTextNode("Acetylation"); rule_name_element.appendChild(rule_name_text); QDomElement outcome_element = document.createElement("outcome"); QDomText outcome_text = document.createTextNode("LOST"); outcome_element.appendChild(outcome_text); chemgrouprule_element.appendChild(entity_element); chemgrouprule_element.appendChild(rule_name_element); chemgrouprule_element.appendChild(outcome_element); mnmchemgroup_element.appendChild(name_element); mnmchemgroup_element.appendChild(pka_element); mnmchemgroup_element.appendChild(acidcharged_element); mnmchemgroup_element.appendChild(polrule_element); mnmchemgroup_element.appendChild(chemgrouprule_element); document.appendChild(mnmchemgroup_element); REQUIRE(chemical_group.renderXmlMnmElement(mnmchemgroup_element)); THEN("The object is valid and the member data are set correctly") { REQUIRE(chemical_group.isValid()); REQUIRE(chemical_group.getName().toStdString() == "N-term NH2"); REQUIRE_THAT(chemical_group.getPka(), Catch::Matchers::WithinAbs(9.6, 0.001)); REQUIRE(chemical_group.isAcidCharged()); REQUIRE(chemical_group.getPolRule() == Enums::ChemicalGroupTrapped::LEFT); REQUIRE( chemical_group.getRulesCstRef().front()->getName().toStdString() == "Acetylation"); REQUIRE( chemical_group.getRulesCstRef().front()->getEntity().toStdString() == "LE_PLM_MODIF"); REQUIRE(chemical_group.getRulesCstRef().front()->getFate() == Enums::ChemicalGroupFate::LOST); } } } } SCENARIO( "ChemicalGroup objects can be created by reading a mdfchemgroup XML element", "[ChemicalGroup]") { WHEN("Constructing an empty ChemicalGroup") { ChemicalGroup chemical_group; AND_WHEN("Initializing it with a XML element") { // QString dom_string = // "Phosphorylation1.2FALSE"; QDomDocument document; QDomElement mdfchemgroup_element = document.createElement("mdfchemgroup"); QDomElement name_element = document.createElement("name"); QDomText name_text = document.createTextNode("Phosphorylation"); name_element.appendChild(name_text); QDomElement pka_element = document.createElement("pka"); QDomText pka_text = document.createTextNode("1.2"); pka_element.appendChild(pka_text); QDomElement acidcharged_element = document.createElement("acidcharged"); QDomText acidcharged_text = document.createTextNode("FALSE"); acidcharged_element.appendChild(acidcharged_text); mdfchemgroup_element.appendChild(name_element); mdfchemgroup_element.appendChild(pka_element); mdfchemgroup_element.appendChild(acidcharged_element); document.appendChild(mdfchemgroup_element); REQUIRE(chemical_group.renderXmlMdfElement(mdfchemgroup_element)); THEN("The object is valid and the member data are set correctly") { REQUIRE(chemical_group.isValid()); REQUIRE(chemical_group.getName().toStdString() == "Phosphorylation"); REQUIRE_THAT(chemical_group.getPka(), Catch::Matchers::WithinAbs(1.2, 0.001)); REQUIRE_FALSE(chemical_group.isAcidCharged()); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_ChemicalGroupRule.cpp000664 001750 001750 00000002162 15100504560 023715 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("ChemicalGroupRule object can be constructed empty", "[ChemicalGroupRule]") { WHEN("Constructing an empty ChemicalGroupRule") { ChemicalGroupRule chemical_group_rule; THEN("The object is invalid") { REQUIRE_FALSE(chemical_group_rule.isValid()); } AND_WHEN("Setting the name, the entity and the fate") { chemical_group_rule.setName("Acetylation"); chemical_group_rule.setEntity("LE_PLM_MODIF"); chemical_group_rule.setFate(Enums::ChemicalGroupFate::LOST); THEN("The ChemicalGroupRule is valid.") { REQUIRE(chemical_group_rule.isValid()); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CleavageAgent.cpp000664 001750 001750 00000065011 15100504560 023033 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_cleavage_agent_1_letter("protein-1-letter", 1); TestUtils test_utils_cleavage_agent_3_letters("protein-3-letters", 1); ErrorList error_list_cleavage_agent; SCENARIO( "CleavageAgent objects can be constructed empty and then initialized " "piecemeal -until they are valid", "[CleavageAgent]") { test_utils_cleavage_agent_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_agent_1_letter.msp_polChemDef; GIVEN("A CleavageAgent constructed with no parameter at all") { CleavageAgent cleavage_agent; REQUIRE_FALSE(cleavage_agent.isValid()); REQUIRE_FALSE(cleavage_agent.validate(error_list_cleavage_agent)); WHEN("The PolChemDef is set with the setter") { cleavage_agent.setPolchemDefCstSPtr(pol_chem_def_csp); THEN( "The CleavageAgent is still invalid and does not validate successfully") { REQUIRE(cleavage_agent.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE_FALSE(cleavage_agent.isValid()); REQUIRE_FALSE(cleavage_agent.validate(error_list_cleavage_agent)); } AND_WHEN("The name is set with the setter") { cleavage_agent.setName("Trypsin"); REQUIRE(cleavage_agent.getName().toStdString() == "Trypsin"); THEN( "The CleavageAgent is still invalid and does not validate " "successfully") { REQUIRE_FALSE(cleavage_agent.isValid()); REQUIRE_FALSE(cleavage_agent.validate(error_list_cleavage_agent)); } AND_WHEN("The pattern is set") { cleavage_agent.setPattern("K/;-K/P;R/"); THEN( "The CleavageAgent is fully initialized with proper CleavageMotif " "objects (although no CleavageRule objects)") { QString expected_text = "Trypsin - K/;-K/P;R/ - 3 motifs - 0 rules"; REQUIRE(cleavage_agent.toString().toStdString() == expected_text.toStdString()); REQUIRE(cleavage_agent.isValid()); REQUIRE(cleavage_agent.validate(error_list_cleavage_agent)); REQUIRE(cleavage_agent.getMotifsCstRef().size() == 3); REQUIRE( cleavage_agent.getMotifsRef().at(0)->getMotif().toStdString() == "K"); REQUIRE(cleavage_agent.getMotifsCstRef() .at(0) ->getMotif() .toStdString() == "K"); REQUIRE( cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate( error_list_cleavage_agent)); REQUIRE(cleavage_agent.getMotifsCstRef() .at(1) ->getMotif() .toStdString() == "KP"); REQUIRE( cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() == Enums::CleavageAction::NO_CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->validate( error_list_cleavage_agent)); REQUIRE(cleavage_agent.getMotifsCstRef() .at(2) ->getMotif() .toStdString() == "R"); REQUIRE( cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->validate( error_list_cleavage_agent)); } } } } } } SCENARIO( "CleavageAgent objects can be copy- or assignment-constructed and compared", "[CleavageAgent]") { test_utils_cleavage_agent_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_agent_1_letter.msp_polChemDef; GIVEN("A CleavageAgent constructed with full parameters (with CleavageRules)") { CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/"); CleavageRuleSPtr cleavage_rule_sp = std::make_shared( pol_chem_def_csp, "Homoseryl", "A", "+CH3COOH", "M", "-CH2S+O"); cleavage_agent.getRulesRef().push_back(cleavage_rule_sp); cleavage_rule_sp = std::make_shared(pol_chem_def_csp, "Heteroseryl", "C", "+CH3COOH+CH3COOH", "N", "-CH2S+O-CH2S+O"); cleavage_agent.getRulesRef().push_back(cleavage_rule_sp); THEN("The CleavageAgent is valid and validates successfully") { REQUIRE(cleavage_agent.isValid()); REQUIRE(cleavage_agent.validate(error_list_cleavage_agent)); REQUIRE(cleavage_agent.getMotifsCstRef().size() == 3); REQUIRE( cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() == "K"); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate( error_list_cleavage_agent)); REQUIRE( cleavage_agent.getMotifsCstRef().at(1)->getMotif().toStdString() == "KP"); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() == Enums::CleavageAction::NO_CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(1)->validate( error_list_cleavage_agent)); REQUIRE( cleavage_agent.getMotifsCstRef().at(2)->getMotif().toStdString() == "R"); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(2)->validate( error_list_cleavage_agent)); REQUIRE(cleavage_agent.getRulesCstRef().size() == 2); } WHEN( "Two other CleavageAgent are either copy-constructed or " "assignment-copied") { CleavageAgent other_cleavage_agent(cleavage_agent); CleavageAgent new_cleavage_agent; new_cleavage_agent = other_cleavage_agent; THEN("They are identical to the original one") { REQUIRE(new_cleavage_agent.isValid()); REQUIRE(new_cleavage_agent.validate(error_list_cleavage_agent)); REQUIRE(new_cleavage_agent.getMotifsCstRef().size() == 3); REQUIRE(new_cleavage_agent.getMotifsCstRef() .at(0) ->getMotif() .toStdString() == "K"); REQUIRE( new_cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(0)->isValid()); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(0)->validate( error_list_cleavage_agent)); REQUIRE(new_cleavage_agent.getMotifsCstRef() .at(1) ->getMotif() .toStdString() == "KP"); REQUIRE( new_cleavage_agent.getMotifsCstRef().at(1)->getCleavageAction() == Enums::CleavageAction::NO_CLEAVE); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(1)->getOffset() == 1); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(1)->isValid()); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(1)->validate( error_list_cleavage_agent)); REQUIRE(new_cleavage_agent.getMotifsCstRef() .at(2) ->getMotif() .toStdString() == "R"); REQUIRE( new_cleavage_agent.getMotifsCstRef().at(2)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(2)->getOffset() == 1); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(2)->isValid()); REQUIRE(new_cleavage_agent.getMotifsCstRef().at(2)->validate( error_list_cleavage_agent)); REQUIRE(new_cleavage_agent.getRulesCstRef().size() == 2); } THEN("The comparison operators return correct results") { REQUIRE(new_cleavage_agent == cleavage_agent); REQUIRE_FALSE(new_cleavage_agent != cleavage_agent); REQUIRE(new_cleavage_agent == new_cleavage_agent); REQUIRE_FALSE(new_cleavage_agent != new_cleavage_agent); } AND_WHEN("One CleavageAgent's name is modified") { cleavage_agent.setName("OtherName"); THEN("The comparison operators return correct results") { REQUIRE_FALSE(new_cleavage_agent == cleavage_agent); REQUIRE(new_cleavage_agent != cleavage_agent); } } AND_WHEN("One CleavageAgent's motifs is removed") { cleavage_agent.setName("Trypsin"); cleavage_agent.getMotifsRef().erase( cleavage_agent.getMotifsRef().begin()); THEN("The comparison operators return correct results") { REQUIRE_FALSE(new_cleavage_agent == cleavage_agent); REQUIRE(new_cleavage_agent != cleavage_agent); } } AND_WHEN("One CleavageAgent's motif is modified") { cleavage_agent = new_cleavage_agent; REQUIRE(new_cleavage_agent == cleavage_agent); cleavage_agent.getMotifsRef().front()->setMotif("M"); REQUIRE( cleavage_agent.getMotifsRef().front()->getMotif().toStdString() == "M"); THEN( "The two CleavageAgent instances are the same because the motif is a " "vector of MonomerSPtr and the modification is done to the managed " "object") { // The cleavage agents are still the same, because the // Motif is actually MonomerSPtr which are shared. REQUIRE(new_cleavage_agent == cleavage_agent); REQUIRE_FALSE(new_cleavage_agent != cleavage_agent); } } AND_WHEN("One CleavageAgent's cleavage rule is modified") { cleavage_agent = new_cleavage_agent; REQUIRE(new_cleavage_agent == cleavage_agent); cleavage_agent.getRulesRef().front()->setLeftCode("G"); REQUIRE( cleavage_agent.getRulesRef().front()->getLeftCode().toStdString() == "G"); THEN( "The two CleavageAgent instances are the same because the rules are " "stored as a vector of CleavageRuleSPtr and the modification is done " "to the managed object") { REQUIRE(new_cleavage_agent == cleavage_agent); REQUIRE_FALSE(new_cleavage_agent != cleavage_agent); } } AND_WHEN("One CleavageAgent's cleavage rule is removed") { cleavage_agent = new_cleavage_agent; REQUIRE(new_cleavage_agent == cleavage_agent); cleavage_agent.getRulesRef().erase( cleavage_agent.getRulesRef().begin()); THEN("The two CleavageAgent instances are different") { REQUIRE_FALSE(new_cleavage_agent == cleavage_agent); REQUIRE(new_cleavage_agent != cleavage_agent); } } } } } SCENARIO( "CleavageAgent objects can be initialized from the polymer chemistry " "definition", "[CleavageAgent]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_agent_1_letter.msp_polChemDef; WHEN("A CleavageAgent is initialized with name") { CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "NOT_SET"); THEN("The matching CleavageAgent can be retrieved from PolChemDef") { CleavageAgentCstSPtr cleavage_agent_csp = cleavage_agent.getFromPolChemDefByName(); REQUIRE(cleavage_agent_csp != nullptr); REQUIRE(cleavage_agent_csp->getName().toStdString() == "Trypsin"); } WHEN("A CleavageAgent is initialized with no name") { CleavageAgent cleavage_agent(pol_chem_def_csp, "", "NOT_SET"); THEN("The matching CleavageAgent can not be retrieved from PolChemDef") { CleavageAgentCstSPtr cleavage_agent_csp = cleavage_agent.getFromPolChemDefByName(); REQUIRE(cleavage_agent_csp == nullptr); } } WHEN("A CleavageAgent is initialized with proper name") { CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "NOT_SET"); THEN("The matching CleavageAgent is not known to the PolChemDef") { REQUIRE(cleavage_agent.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); } } WHEN("A CleavageAgent is initialized with no name") { CleavageAgent cleavage_agent(pol_chem_def_csp, "", "NOT_SET"); THEN("The matching CleavageAgent is not known to the PolChemDef") { REQUIRE(cleavage_agent.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN); } } } } SCENARIO( "CleavageAgent objects can be initialized from a XML element and " "can export themselves as ", "[CleavageAgent]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_agent_1_letter.msp_polChemDef; // int cls_element_index = 0; // int name_element_index = 1; // int name_text_index = 2; // int pattern_element_index = 3; // int pattern_text_index = 4; // int clr_element_index = 5; // int clr_name_element_index = 6; // int clr_name_text_index = 7; // int le_mnm_code_element_index = 8; // int le_mnm_code_text_index = 9; // int le_formula_element_index = 10; // int le_formula_text_index = 11; // int re_mnm_code_element_index = 12; // int re_mnm_code_text_index = 13; // int re_formula_element_index = 14; // int re_formula_text_index = 15; // FAKE cls! // // CyanogenBromide // M/A // // M // -C1H2S1+O1 // A // -CH3COOH // // QStringList dom_strings{"cls", // 0 "name", // 1 "CyanogenBromide", // 2 "pattern", // 3 "M/A", // 4 "clr", // 5 "name", // 6 "Homoseryl", // 7 "le-mnm-code", // 8 "M", // 9 "le-formula", // 10 "-C1H2S1+O1", // 11 "re-mnm-code", // 12 "A", // 13 "re-formula", // 14 "-CH3COOH"}; // 15 QDomDocument document = test_utils_cleavage_agent_1_letter.craftClsDomDocument(dom_strings); QDomElement cleavage_agent_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare // them. There must be a bug somewhere because with the original output, at // the screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); // We need two versions: cls for version 1 and cla for version 2. QString expected_cls_string = "CyanogenBromideM/AHomoserylM-C1H2S1+O1A-CH3COOH"; expected_cls_string = Utils::unspacify(expected_cls_string); QString expected_cla_string = "CyanogenBromideM/AHomoserylM-C1H2S1+O1A-CH3COOH"; expected_cla_string = Utils::unspacify(expected_cla_string); REQUIRE(document_string.toStdString() == expected_cls_string.toStdString()); WHEN("Creating a CleavageAgent only with PolChemDef") { CleavageAgent cleavage_agent(pol_chem_def_csp); AND_WHEN("Initializing it with an XML element") { REQUIRE(cleavage_agent.renderXmlClsElement(cleavage_agent_element, /*version*/ 1)); THEN("The initalized CleavageAgent is valid and validates successfully") { THEN("The CleavageAgent is valid and validates successfully") { REQUIRE(cleavage_agent.isValid()); REQUIRE(cleavage_agent.validate(error_list_cleavage_agent)); REQUIRE(cleavage_agent.getName().toStdString() == "CyanogenBromide"); REQUIRE(cleavage_agent.getPattern().toStdString() == "M/A"); REQUIRE(cleavage_agent.getMotifsCstRef().size() == 1); REQUIRE( cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() == "MA"); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate( error_list_cleavage_agent)); REQUIRE(cleavage_agent.getRulesCstRef().size() == 1); REQUIRE(cleavage_agent.getCleavageRuleIndexByName("Homoseryl") == 0); CleavageRuleCstSPtr cleavage_rule_csp = cleavage_agent.getCleavageRuleCstSPtrByName("Homoseryl"); REQUIRE(cleavage_rule_csp != nullptr); REQUIRE(cleavage_rule_csp->validate(error_list_cleavage_agent)); REQUIRE(cleavage_rule_csp->getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule_csp->getLeftCode().toStdString() == "M"); REQUIRE(cleavage_rule_csp->getLeftFormula() .getActionFormula() .toStdString() == "-C1H2S1+O1"); REQUIRE(cleavage_rule_csp->getRightCode().toStdString() == "A"); REQUIRE(cleavage_rule_csp->getRightFormula() .getActionFormula() .toStdString() == "-CH3COOH"); CleavageRuleSPtr cleavage_rule_sp = cleavage_agent.getCleavageRuleSPtrByName("Homoseryl"); REQUIRE(cleavage_rule_sp != nullptr); REQUIRE(cleavage_rule_sp->validate(error_list_cleavage_agent)); REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule_sp->getLeftCode().toStdString() == "M"); REQUIRE(cleavage_rule_sp->getLeftFormula() .getActionFormula() .toStdString() == "-C1H2S1+O1"); REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "A"); REQUIRE(cleavage_rule_sp->getRightFormula() .getActionFormula() .toStdString() == "-CH3COOH"); } } AND_WHEN("Exporting itself as a XML element") { QString xml_text = cleavage_agent.formatXmlClaElement(/*offset*/ 1); xml_text = Utils::unspacify(xml_text); THEN("The text must be identical to the expected XML string") { REQUIRE(xml_text.toStdString() == expected_cla_string.toStdString()); } } } } } SCENARIO( "CleavageAgent objects can be initialized from a XML element and " "can " "export themselves as such also", "[CleavageAgent]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_agent_1_letter.msp_polChemDef; // int cla_element_index = 0; // int name_element_index = 1; // int name_text_index = 2; // int pattern_element_index = 3; // int pattern_text_index = 4; // int clr_element_index = 5; // int clr_name_element_index = 6; // int clr_name_text_index = 7; // int le_mnm_code_element_index = 8; // int le_mnm_code_text_index = 9; // int le_formula_element_index = 10; // int le_formula_text_index = 11; // int re_mnm_code_element_index = 12; // int re_mnm_code_text_index = 13; // int re_formula_element_index = 14; // int re_formula_text_index = 15; // FAKE cla! // // CyanogenBromide // M/A // // M // -C1H2S1+O1 // A // -CH3COOH // // QStringList dom_strings{"cla", // 0 "name", // 1 "CyanogenBromide", // 2 "pattern", // 3 "M/A", // 4 "clr", // 5 "name", // 6 "Homoseryl", // 7 "le-mnm-code", // 8 "M", // 9 "le-formula", // 10 "-C1H2S1+O1", // 11 "re-mnm-code", // 12 "A", // 13 "re-formula", // 14 "-CH3COOH"}; // 15 QDomDocument document = test_utils_cleavage_agent_1_letter.craftClaDomDocument(dom_strings); QDomElement cleavage_agent_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare // them. There must be a bug somewhere because with the original output, at // the screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); QString expected_cla_string = "CyanogenBromideM/AHomoserylM-C1H2S1+O1A-CH3COOH"; expected_cla_string = Utils::unspacify(expected_cla_string); REQUIRE(document_string.toStdString() == expected_cla_string.toStdString()); WHEN("Creating a CleavageAgent only with PolChemDef") { CleavageAgent cleavage_agent(pol_chem_def_csp); AND_WHEN("Initializing it with an XML element") { REQUIRE(cleavage_agent.renderXmlClaElement(cleavage_agent_element, /*version*/ 1)); THEN("The initalized CleavageAgent is valid and validates successfully") { THEN("The CleavageAgent is valid and validates successfully") { REQUIRE(cleavage_agent.isValid()); REQUIRE(cleavage_agent.validate(error_list_cleavage_agent)); REQUIRE(cleavage_agent.getName().toStdString() == "CyanogenBromide"); REQUIRE(cleavage_agent.getPattern().toStdString() == "M/A"); REQUIRE(cleavage_agent.getMotifsCstRef().size() == 1); REQUIRE( cleavage_agent.getMotifsCstRef().at(0)->getMotif().toStdString() == "MA"); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getCleavageAction() == Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->getOffset() == 1); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->isValid()); REQUIRE(cleavage_agent.getMotifsCstRef().at(0)->validate( error_list_cleavage_agent)); REQUIRE(cleavage_agent.getRulesCstRef().size() == 1); CleavageRuleSPtr cleavage_rule_sp = cleavage_agent.getCleavageRuleSPtrByName("Homoseryl"); REQUIRE(cleavage_rule_sp != nullptr); REQUIRE(cleavage_rule_sp->validate(error_list_cleavage_agent)); REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule_sp->getLeftCode().toStdString() == "M"); REQUIRE(cleavage_rule_sp->getLeftFormula() .getActionFormula() .toStdString() == "-C1H2S1+O1"); REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "A"); REQUIRE(cleavage_rule_sp->getRightFormula() .getActionFormula() .toStdString() == "-CH3COOH"); } } AND_WHEN("Exporting itself as a XML element") { QString xml_text = cleavage_agent.formatXmlClaElement(/*offset*/ 1); xml_text = Utils::unspacify(xml_text); THEN("The text must be identical to the expected XML string") { REQUIRE(xml_text.toStdString() == expected_cla_string.toStdString()); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CleavageConfig.cpp000664 001750 001750 00000017257 15100504560 023213 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_cleavage_config_1_letter("protein-1-letter", 1); TestUtils test_utils_cleavage_config_3_letters("protein-3-letters", 1); ErrorList error_list_cleavage_config; SCENARIO("Construction of a CleavageConfig object", "[CleavageConfig]") { test_utils_cleavage_config_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_config_1_letter.msp_polChemDef; WHEN("A CleavageConfig constructed with all params") { CleavageConfig cleavage_config(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/", 0, /*is_sequence_embedded*/ true); cleavage_config.setStartIonizeLevel(1); cleavage_config.setStopIonizeLevel(10); THEN("All the members are set accordingly") { REQUIRE(cleavage_config.getPartials() == 0); REQUIRE(cleavage_config.getStartIonizeLevel() == 1); REQUIRE(cleavage_config.getStopIonizeLevel() == 10); REQUIRE(cleavage_config.isSequenceEmbedded()); } AND_WHEN("Some data members are modified") { cleavage_config.setPartials(1); cleavage_config.setSequenceEmbedded(false); THEN("The corresponding members are updated") { REQUIRE(cleavage_config.getPartials() == 1); REQUIRE(cleavage_config.getStartIonizeLevel() == 1); REQUIRE(cleavage_config.getStopIonizeLevel() == 10); REQUIRE_FALSE(cleavage_config.isSequenceEmbedded()); } AND_WHEN( "Another CleavageConfig instance is created by copy-construction or " "assignment") { CleavageConfig other_cleavage_config(cleavage_config); CleavageConfig another_cleavage_config; another_cleavage_config = other_cleavage_config; THEN( "The other CleavageConfig instances are identical to the initial one") { REQUIRE(another_cleavage_config.getPartials() == 1); REQUIRE(another_cleavage_config.getStartIonizeLevel() == 1); REQUIRE(another_cleavage_config.getStopIonizeLevel() == 10); REQUIRE_FALSE(another_cleavage_config.isSequenceEmbedded()); } } } } } SCENARIO("CleavageConfig objects can be compared with operators ==() and !=().", "[CleavageConfig]") { test_utils_cleavage_config_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_config_1_letter.msp_polChemDef; WHEN("Two identical CleavageConfig objects are constructed with all params") { CleavageConfig cleavage_config_1(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/", 0, /*is_sequence_embedded*/ true); cleavage_config_1.setStartIonizeLevel(1); cleavage_config_1.setStopIonizeLevel(10); CleavageConfig cleavage_config_2(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/", 0, /*is_sequence_embedded*/ true); // Choose on purpose this variant :-) cleavage_config_2.setIonizeLevels(1, 10); THEN("All the members are set accordingly") { REQUIRE(cleavage_config_1.getName().toStdString() == "Trypsin"); REQUIRE(cleavage_config_1.getPattern().toStdString() == "K/;-K/P;R/"); REQUIRE(cleavage_config_1.isValid()); REQUIRE(cleavage_config_1.getPartials() == 0); REQUIRE(cleavage_config_1.getStartIonizeLevel() == 1); REQUIRE(cleavage_config_1.getStopIonizeLevel() == 10); REQUIRE(cleavage_config_1.isSequenceEmbedded()); REQUIRE(cleavage_config_2.getName().toStdString() == "Trypsin"); REQUIRE(cleavage_config_2.getPattern().toStdString() == "K/;-K/P;R/"); REQUIRE(cleavage_config_2.isValid()); REQUIRE(cleavage_config_2.getPartials() == 0); REQUIRE(cleavage_config_2.getStartIonizeLevel() == 1); REQUIRE(cleavage_config_2.getStopIonizeLevel() == 10); REQUIRE(cleavage_config_2.isSequenceEmbedded()); } AND_THEN("The comparison operators return proper results") { REQUIRE(cleavage_config_1 == cleavage_config_2); REQUIRE_FALSE(cleavage_config_1 != cleavage_config_2); } } } SCENARIO("CleavageConfig objects can be copy- or assignment- constructed", "[CleavageConfig]") { test_utils_cleavage_config_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_config_1_letter.msp_polChemDef; WHEN("A CleavageConfig constructed with all params") { CleavageConfig cleavage_config(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/", 0, /*is_sequence_embedded*/ true); cleavage_config.setStartIonizeLevel(1); cleavage_config.setStopIonizeLevel(10); THEN("All the members are set accordingly") { REQUIRE(cleavage_config.getPartials() == 0); REQUIRE(cleavage_config.getStartIonizeLevel() == 1); REQUIRE(cleavage_config.getStopIonizeLevel() == 10); REQUIRE(cleavage_config.isSequenceEmbedded()); } AND_WHEN("New CleavageConfig objects are instantiated") { CleavageConfig another_cleavage_config(cleavage_config); CleavageConfig other_cleavage_config; other_cleavage_config = another_cleavage_config; THEN("The copies must be identical to the original object") { REQUIRE(other_cleavage_config.getPartials() == 0); REQUIRE(other_cleavage_config.getStartIonizeLevel() == 1); REQUIRE(other_cleavage_config.getStopIonizeLevel() == 10); REQUIRE(other_cleavage_config.isSequenceEmbedded()); REQUIRE(other_cleavage_config == cleavage_config); REQUIRE_FALSE(other_cleavage_config != cleavage_config); } } } } SCENARIO("Construction of an empty CleavageConfig object", "[CleavageConfig]") { test_utils_cleavage_config_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_config_1_letter.msp_polChemDef; WHEN("An empty CleavageConfig constructed") { CleavageConfig cleavage_config; AND_WHEN("A Cleavage agent is used to initialize it") { CleavageAgent cleavage_agent(pol_chem_def_csp, "Trypsin", "K/;-K/P;R/"); cleavage_config.setCleavageAgent(cleavage_agent); AND_WHEN("The setter functions are used to configure the CleavageConfig") { cleavage_config.setPartials(0); cleavage_config.setSequenceEmbedded(false); cleavage_config.setStartIonizeLevel(1); cleavage_config.setStopIonizeLevel(10); THEN("The member data are correctly set") { REQUIRE(cleavage_config.getPartials() == 0); REQUIRE(cleavage_config.getStartIonizeLevel() == 1); REQUIRE(cleavage_config.getStopIonizeLevel() == 10); REQUIRE_FALSE(cleavage_config.isSequenceEmbedded()); REQUIRE(static_cast(cleavage_config) == cleavage_agent); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CleavageMotif.cpp000664 001750 001750 00000034356 15100504560 023063 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_cleavage_motif_1_letter("protein-1-letter", 1); TestUtils test_utils_cleavage_motif_3_letters("protein-3-letters", 1); ErrorList error_list_cleavage_motif; SCENARIO( "CleavageMotif objects can be constructed empty and then initialized " "piecemeal " "until they are valid", "[CleavageMotif]") { test_utils_cleavage_motif_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_motif_1_letter.msp_polChemDef; WHEN("Constructing a CleavageMotif with no valid parameter") { CleavageMotif cleavage_motif(nullptr); THEN("The CleavageMotif is invalid and does not validate successfully") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); } AND_WHEN("Setting the PolChemDef") { cleavage_motif.setPolChemDefCstSPtr(pol_chem_def_csp); THEN("The CleavageMotif is still invalid") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); } AND_WHEN("Setting the Motif") { cleavage_motif.setMotif("KP"); THEN( "The CleavageMotif is still invalid although with proper monomer " "container") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "K"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "P"); } AND_WHEN("Setting the offset") { cleavage_motif.setOffset(1); THEN( "The CleavageMotif is still invalid without proper " "Enums::CleavageAction " "set") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "K"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "P"); REQUIRE(cleavage_motif.getOffset() == 1); } AND_WHEN("Setting the Enums::CleavageAction") { cleavage_motif.setCleavageAction(Enums::CleavageAction::NO_CLEAVE); THEN("The CleavageMotif is valid with proper partial member data") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getOffset() == 1); } } } } } } } SCENARIO( "CleavageMotif objects can be constructed with parameters to fully " "initialize " "them in one go", "[CleavageMotif]") { test_utils_cleavage_motif_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_motif_1_letter.msp_polChemDef; WHEN("Constructing a CleavageMotif with all valid parameters") { CleavageMotif cleavage_motif( pol_chem_def_csp, "KP", 1, Enums::CleavageAction::NO_CLEAVE); THEN("The CleavageMotif must be valid and validates successfully") { REQUIRE(cleavage_motif.isValid()); error_list_cleavage_motif.clear(); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(error_list_cleavage_motif.size() == 0); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "K"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "P"); REQUIRE(cleavage_motif.getOffset() == 1); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::NO_CLEAVE); } } } SCENARIO("CleavageMotif objects can be initialized with a cleavage site", "[CleavageMotif]") { test_utils_cleavage_motif_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_motif_1_letter.msp_polChemDef; WHEN("Constructing a CleavageMotif with only PolChemDef parameter") { CleavageMotif cleavage_motif(pol_chem_def_csp); AND_WHEN("Setting a cleavage site") { REQUIRE(cleavage_motif.parseSite("K/P") == 2); THEN( "The CleavageMotif is not valid because the cleavage operation is not " "defined") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "K"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "P"); REQUIRE(cleavage_motif.getOffset() == 1); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::NOT_SET); } AND_WHEN("The is-for-cleavage bit is set") { cleavage_motif.setCleavageAction(Enums::CleavageAction::NO_CLEAVE); THEN( "The CleavageMotif is fully initialized, valid and validates " "successfully") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); } } } } } SCENARIO("CleavageMotif objects are checked for validity", "[CleavageMotif]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_motif_3_letters.msp_polChemDef; WHEN("Constructing a CleavageMotif with only PolChemDef parameter") { CleavageMotif cleavage_motif(pol_chem_def_csp); AND_WHEN("Setting a cleavage site with 3-letters code") { REQUIRE(cleavage_motif.parseSite("Lys/Pro") == 2); THEN( "The CleavageMotif is not valid because the cleavage operation is not " "defined") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 2); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "Lys"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "Pro"); REQUIRE(cleavage_motif.getOffset() == 1); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::NOT_SET); } AND_WHEN("The is-for-cleavage bit is set") { cleavage_motif.setCleavageAction(Enums::CleavageAction::NO_CLEAVE); THEN( "The CleavageMotif is fully initialized, valid and validates " "successfully") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); } } } AND_WHEN("Setting a cleavage site with 3-letters code") { REQUIRE(cleavage_motif.parseSite("LysArgMet/") == 3); THEN( "The CleavageMotif is not valid because the cleavage operation is not " "defined") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 3); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "Lys"); REQUIRE(cleavage_motif.getMonomersCstRef().at(1)->getCode() == "Arg"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "Met"); REQUIRE(cleavage_motif.getOffset() == 3); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::NOT_SET); } AND_WHEN("The is-for-cleavage bit is set") { cleavage_motif.setCleavageAction(Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::CLEAVE); THEN( "The CleavageMotif is fully initialized, valid and validates " "successfully") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); } } } AND_WHEN("Setting a cleavage site with 3-letters code") { REQUIRE(cleavage_motif.parseSite("/AspGluTrpLysArgMet") == 6); THEN( "The CleavageMotif is not valid because the cleavage operation is not " "defined") { REQUIRE_FALSE(cleavage_motif.isValid()); REQUIRE_FALSE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 6); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "Asp"); REQUIRE(cleavage_motif.getMonomersCstRef().at(1)->getCode() == "Glu"); REQUIRE(cleavage_motif.getMonomersCstRef().at(2)->getCode() == "Trp"); REQUIRE(cleavage_motif.getMonomersCstRef().at(3)->getCode() == "Lys"); REQUIRE(cleavage_motif.getMonomersCstRef().at(4)->getCode() == "Arg"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "Met"); REQUIRE(cleavage_motif.getOffset() == 0); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::NOT_SET); } AND_WHEN("The is-for-cleavage bit is set") { cleavage_motif.setCleavageAction(Enums::CleavageAction::CLEAVE); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::CLEAVE); THEN( "The CleavageMotif is fully initialized, valid and validates " "successfully") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); } } } } } SCENARIO( "CleavageMotif objects can be copied by construction or assignment and then " "compared", "[CleavageMotif]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_motif_3_letters.msp_polChemDef; WHEN("Constructing a fully initialized CleavageMotif") { CleavageMotif cleavage_motif( pol_chem_def_csp, "/AspGluTrpLysArgMet", 0, Enums::CleavageAction::CLEAVE); THEN("The CleavageMotif is valid and fully initialized") { REQUIRE(cleavage_motif.isValid()); REQUIRE(cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(cleavage_motif.getMonomersCstRef().size() == 6); REQUIRE(cleavage_motif.getMonomersCstRef().front()->getCode() == "Asp"); REQUIRE(cleavage_motif.getMonomersCstRef().at(1)->getCode() == "Glu"); REQUIRE(cleavage_motif.getMonomersCstRef().at(2)->getCode() == "Trp"); REQUIRE(cleavage_motif.getMonomersCstRef().at(3)->getCode() == "Lys"); REQUIRE(cleavage_motif.getMonomersCstRef().at(4)->getCode() == "Arg"); REQUIRE(cleavage_motif.getMonomersCstRef().back()->getCode() == "Met"); REQUIRE(cleavage_motif.getOffset() == 0); REQUIRE(cleavage_motif.getCleavageAction() == Enums::CleavageAction::CLEAVE); } AND_WHEN("A new CleavageMotif is allocated by copy-construction") { CleavageMotif new_cleavage_motif(cleavage_motif); THEN("The CleavageMotif is valid and fully initialized") { REQUIRE(new_cleavage_motif.isValid()); REQUIRE(new_cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(new_cleavage_motif.getMonomersCstRef().size() == 6); REQUIRE(new_cleavage_motif.getMonomersCstRef().front()->getCode() == "Asp"); REQUIRE(new_cleavage_motif.getMonomersCstRef().at(1)->getCode() == "Glu"); REQUIRE(new_cleavage_motif.getMonomersCstRef().at(2)->getCode() == "Trp"); REQUIRE(new_cleavage_motif.getMonomersCstRef().at(3)->getCode() == "Lys"); REQUIRE(new_cleavage_motif.getMonomersCstRef().at(4)->getCode() == "Arg"); REQUIRE(new_cleavage_motif.getMonomersCstRef().back()->getCode() == "Met"); REQUIRE(new_cleavage_motif.getOffset() == 0); REQUIRE(new_cleavage_motif.getCleavageAction() == Enums::CleavageAction::CLEAVE); AND_THEN("The comparisons yield expected results") { REQUIRE(new_cleavage_motif == cleavage_motif); REQUIRE_FALSE(new_cleavage_motif != cleavage_motif); } } } AND_WHEN("A new CleavageMotif is allocated by assignment") { CleavageMotif new_new_cleavage_motif(nullptr); new_new_cleavage_motif = cleavage_motif; THEN("The CleavageMotif is valid and fully initialized") { REQUIRE(new_new_cleavage_motif.isValid()); REQUIRE(new_new_cleavage_motif.validate(error_list_cleavage_motif)); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().size() == 6); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().front()->getCode() == "Asp"); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().at(1)->getCode() == "Glu"); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().at(2)->getCode() == "Trp"); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().at(3)->getCode() == "Lys"); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().at(4)->getCode() == "Arg"); REQUIRE(new_new_cleavage_motif.getMonomersCstRef().back()->getCode() == "Met"); REQUIRE(new_new_cleavage_motif.getOffset() == 0); REQUIRE(new_new_cleavage_motif.getCleavageAction() == Enums::CleavageAction::CLEAVE); AND_THEN("The comparisons yield expected results") { REQUIRE(new_new_cleavage_motif == cleavage_motif); REQUIRE_FALSE(new_new_cleavage_motif != cleavage_motif); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CleavageRule.cpp000664 001750 001750 00000035415 15100504560 022711 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_cleavage_rule_1_letter("protein-1-letter", 1); TestUtils test_utils_cleavage_rule_3_letters("protein-3-letters", 1); ErrorList error_list_cleavage_rule; SCENARIO( "CleavageRule objects can be constructed empty and then initialized " "piecemeal " "until they are valid", "[CleavageRule]") { test_utils_cleavage_rule_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_rule_1_letter.msp_polChemDef; WHEN("Constructing a CleavageRule with no valid parameter") { CleavageRule cleavage_rule; THEN("The CleavageRule is invalid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } AND_WHEN( "Setting PolChemDef, a name, correct left/right codes and formulas") { cleavage_rule.setPolchemDefCstSPtr(pol_chem_def_csp); cleavage_rule.setName("Homoseryl"); cleavage_rule.setLeftCode("A"); cleavage_rule.setLeftFormula(Formula("+CH3COOH")); cleavage_rule.setRightCode("M"); cleavage_rule.setRightFormula(Formula("-CH2S+O")); THEN("The CleavageRule is valid and does validate successfully") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); AND_THEN("All the members are set correctly") { REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE( cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } } } } } SCENARIO("CleavageRule objects can be initialized from an XML element", "[CleavageRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_rule_1_letter.msp_polChemDef; // Totally fake clr !!! // // Homoseryl // A // +CH3COOH // M // -CH2S+O // // int clr_element_index = 0; // int name_element_index = 1; // int name_text_index = 2; // int le_mnm_code_element_index = 3; // int le_mnm_code_text_index = 4; // int le_formula_element_index = 5; // int le_formula_text_index = 6; // int re_mnm_code_element_index = 7; // int re_mnm_code_text_index = 8; // int re_formula_element_index = 9; // int re_formula_text_index = 10; QStringList dom_strings{"clr", "name", "Homoseryl", "le-mnm-code", "A", "le-formula", "+CH3COOH", "re-mnm-code", "M", "re-formula", "-CH2S+O"}; QDomDocument document = test_utils_cleavage_rule_1_letter.craftClrDomDocument(dom_strings); QDomElement cleavage_rule_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare them. // There must be a bug somewhere because with the original output, at the // screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); QString expected_string = "\n" " Homoseryl\n" " A\n" " +CH3COOH\n" " M\n" " -CH2S+O\n" " \n"; expected_string = Utils::unspacify(expected_string); REQUIRE(document_string.toStdString() == expected_string.toStdString()); WHEN( "A CleavageRule instance is allocated entirely free of data (only the " "polymer chemistry definition is provided to the constructor)") { CleavageRule cleavage_rule(pol_chem_def_csp, ""); THEN("The CleavageRule instance is not valid and cannot validate") { REQUIRE(cleavage_rule.getName().toStdString() == ""); REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); AND_WHEN( "The CleavageRule instance is asked to render in itself the XML " "element") { REQUIRE(cleavage_rule.renderXmlClrElement(cleavage_rule_element, /*version*/ 1)); THEN("All the members are set correctly") { REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE( cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } } } } } SCENARIO("CleavageRule objects can output themselves to a XML element", "[CleavageRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_rule_1_letter.msp_polChemDef; QString expected_string = "\n" " Homoseryl\n" " A\n" " +CH3COOH\n" " M\n" " -CH2S+O\n" " \n"; expected_string = Utils::unspacify(expected_string); GIVEN("Construction a CleavageRule fully initialized") { CleavageRule cleavage_rule( pol_chem_def_csp, "Homoseryl", "A", "+CH3COOH", "M", "-CH2S+O"); THEN( "The CleavageRule is valid and does validate successfully and all the " "members are set correctly") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); REQUIRE(cleavage_rule.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE(cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } WHEN("The CleavageRule is exported as a XML element") { QString xml_text = cleavage_rule.formatXmlClrElement(/*offset*/ 1); xml_text = Utils::unspacify(xml_text); THEN("The obtained text is as expected") { REQUIRE(xml_text.toStdString() == expected_string.toStdString()); } } } } SCENARIO( "CleavageRule objects can be constructed by copy constructor or by assigment " "and then compared", "[CleavageRule]") { test_utils_cleavage_rule_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_rule_1_letter.msp_polChemDef; GIVEN("Construction a CleavageRule fully initialized") { CleavageRule cleavage_rule( pol_chem_def_csp, "Homoseryl", "A", "+CH3COOH", "M", "-CH2S+O"); THEN( "The CleavageRule is valid and does validate successfully and all the " "members are set correctly") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); REQUIRE(cleavage_rule.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE(cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } WHEN("A new CleavageRule is instantiated by copy-construction") { CleavageRule new_cleavage_rule(cleavage_rule); THEN( "The new CleavageRule is valid and does validate successfully and all " "the members are set correctly") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); REQUIRE(cleavage_rule.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE( cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); AND_THEN("The comparison operator do work properly") { REQUIRE(new_cleavage_rule == cleavage_rule); REQUIRE_FALSE(new_cleavage_rule != cleavage_rule); } } } WHEN("A new CleavageRule is instantiated by assignment") { CleavageRule new_cleavage_rule; new_cleavage_rule = cleavage_rule; THEN( "The new CleavageRule is valid and does validate successfully and all " "the members are set correctly") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); REQUIRE(cleavage_rule.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE( cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); AND_THEN("The comparison operator do work properly") { REQUIRE(new_cleavage_rule == cleavage_rule); REQUIRE_FALSE(new_cleavage_rule != cleavage_rule); } } } } } SCENARIO( "CleavageRule objects are checked for validity at each initialization step", "[CleavageRule]") { test_utils_cleavage_rule_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_cleavage_rule_1_letter.msp_polChemDef; GIVEN("Construction a CleavageRule fully initialized with correct data") { CleavageRule cleavage_rule( pol_chem_def_csp, "Homoseryl", "A", "+CH3COOH", "M", "-CH2S+O"); THEN( "The CleavageRule is valid and does validate successfully and all the " "members are set correctly") { REQUIRE(cleavage_rule.isValid()); REQUIRE(cleavage_rule.validate(error_list_cleavage_rule)); REQUIRE(cleavage_rule.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(cleavage_rule.getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule.getLeftCode().toStdString() == "A"); REQUIRE(cleavage_rule.getLeftFormula().getActionFormula().toStdString() == "+CH3COOH"); REQUIRE(cleavage_rule.getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule.getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } WHEN("An empty name is set") { cleavage_rule.setName(""); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setName("Homoseryl"); } WHEN("A failing left code is set") { cleavage_rule.setLeftCode("B"); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setLeftCode("A"); } WHEN("A failing right code is set") { cleavage_rule.setRightCode("B"); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setRightCode("M"); } WHEN("A failing left formula is set") { cleavage_rule.setLeftFormula(Formula("-h2O")); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setLeftFormula(Formula("+CH3COOH")); } WHEN("A failing right formula is set") { cleavage_rule.setRightFormula(Formula("-h2O")); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setRightFormula(Formula("-CH2S+O")); } WHEN("Having an empty left code and a correct formula") { cleavage_rule.setLeftCode(""); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setLeftCode("A"); } WHEN("Having an empty right code and a correct formula") { cleavage_rule.setRightCode(""); THEN( "The CleavageRule is no more valid and does not validate successfully") { REQUIRE_FALSE(cleavage_rule.isValid()); REQUIRE_FALSE(cleavage_rule.validate(error_list_cleavage_rule)); } cleavage_rule.setRightCode("M"); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Cleaver.cpp000664 001750 001750 00000120646 15100504560 021734 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include #include #include #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_cleaver("protein-1-letter", 1); ErrorList error_list_cleaver; SCENARIO( "Cleaver instances can be copy-constructed", "[Cleaver]") { test_utils_1_letter_cleaver.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cleaver.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("cyan-fluorescent-protein-no-cross-link.mxp"); GIVEN( "A Polymer instance (as a shared pointer) is first created and data are " "loaded from an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 240); AND_GIVEN( "The allocation of CleavageConfig (monoprotonation) and an Ionizer, and " "a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 1); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("A new Cleaver object is copy-contructed") { Cleaver another_cleaver(cleaver); Cleaver other_cleaver; other_cleaver = another_cleaver; THEN("Both Cleaver instances should be identical") { REQUIRE(other_cleaver.getCleaveAgentName().toStdString() == cleaver.getCleaveAgentName().toStdString()); REQUIRE(other_cleaver == cleaver); } } } } } SCENARIO( "Construction of a Cleaver and use with a Polymer without CrossLink " "instances", "[Cleaver]") { test_utils_1_letter_cleaver.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cleaver.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("cyan-fluorescent-protein-no-cross-link.mxp"); GIVEN( "A Polymer instance (as a shared pointer) is first created and data are " "loaded from an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 240); AND_GIVEN( "The allocation of CleavageConfig (monoprotonation) and an Ionizer, and " "a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 1); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 26); REQUIRE(cleaver.getOligomerCollectionRef().size() == 26); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-5]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(461.2723725606, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(461.5355872507, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C19H37N6O7"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "0#26#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[218-240]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(2566.2937091343, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(2568.0082003839, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C116H185N26O35S2"); } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) and an Ionizer, " "and a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 78); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-5]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(461.2723725606, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(461.5355872507, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C19H37N6O7"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "0#26#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[218-240]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(856.1031197330, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(856.6746944402, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C116H187N26O35S2"); } } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) partials = 2 and " "an Ionizer, and a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setPartials(2); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 225); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-5]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(461.2723725606, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(461.5355872507, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C19H37N6O7"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#24#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[212-240]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1102.5568868828, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1103.2683985892, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C146H236N37O46S2"); } } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) partials = 2 and " "an Ionizer, and a Cleaver object and one shortened sequence range") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setPartials(2); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 120); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 117); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-5]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(461.2723725606, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(461.5355872507, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C19H37N6O7"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#12#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[110-121]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(455.9056897620, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(456.1732340835, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C59H99N16O21"); } } } } } } SCENARIO( "Construction of a Cleaver and use with a Polymer without CrossLink " "instances and cleave with CleavageRule instances", "[Cleaver]") { test_utils_1_letter_cleaver.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cleaver.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("horse-myoglobin.mxp"); GIVEN( "A Polymer instance (as a shared pointer) is first created and data are " "loaded from an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 153); AND_GIVEN( "The allocation of CleavageConfig (monoprotonation) and an Ionizer, and " "a " "Cleaver object") { const CleavageAgentCstSPtr cleavage_agent_csp = polymer_sp->getPolChemDefCstSPtr()->getCleavageAgentCstSPtrByName( "CyanogenBromide"); REQUIRE(cleavage_agent_csp != nullptr); REQUIRE(cleavage_agent_csp->getName().toStdString() == "CyanogenBromide"); CleavageConfig m_cleavage_config( *cleavage_agent_csp, /*partials*/ 2, /*seq_embedded*/ true); m_cleavage_config.setIonizeLevels(1, 3); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 152); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); qDebug() << "20241109"; WHEN("The cleavage is asked for") { qDebug() << "20241109"; REQUIRE(cleaver.cleave(/*reset*/ false)); qDebug() << "20241109"; THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 18); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-55]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "CyanogenBromide"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(6231.1943570372, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(6234.9516661300, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C282H434N75O85"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#2#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-153]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "CyanogenBromide"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5647.9961615473, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5651.4605662028, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C769H1215N210O218S2"); } } } } } SCENARIO( "Construction of a Cleaver and use with a Monomer-modified Polymer without " "CrossLink " "instances and cleave with CleavageRule instances", "[Cleaver]") { test_utils_1_letter_cleaver.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cleaver.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("horse-myoglobin.mxp"); GIVEN( "A Polymer instance (as a shared pointer) is first created and data are " "loaded from an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 153); REQUIRE(polymer_sp->modifyMonomer(54, "Oxidation", /*override*/ false)); REQUIRE( polymer_sp->modifyMonomer(107, "Phosphorylation", /*override*/ false)); AND_GIVEN( "The allocation of CleavageConfig (monoprotonation) and an Ionizer, and " "a " "Cleaver object") { const CleavageAgentCstSPtr cleavage_agent_csp = polymer_sp->getPolChemDefCstSPtr()->getCleavageAgentCstSPtrByName( "CyanogenBromide"); REQUIRE(cleavage_agent_csp != nullptr); REQUIRE(cleavage_agent_csp->getName().toStdString() == "CyanogenBromide"); CleavageConfig m_cleavage_config( *cleavage_agent_csp, /*partials*/ 2, /*seq_embedded*/ true); m_cleavage_config.setIonizeLevels(1, 3); CalcOptions calc_options(/*deep_calculation*/ true, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 152); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 18); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-55]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "CyanogenBromide"); REQUIRE(oligomer_sp->isModified()); REQUIRE(oligomer_sp->modifiedMonomerCount() == 1); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(6247.1892716574, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(6250.9510748471, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C282H434N75O86"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#2#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-153]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "CyanogenBromide"); REQUIRE(oligomer_sp->isModified()); REQUIRE(oligomer_sp->modifiedMonomerCount() == 2); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5679.9832433845, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5683.4536789813, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C769H1216N210O222S2P1"); } } } } } SCENARIO( "Construction of a Cleaver and use with a Polymer with CrossLink instances", "[Cleaver]") { test_utils_1_letter_cleaver.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cleaver.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("kunitz-inhibitor-human-cross-links.mxp"); GIVEN( "A Polymer instance (as a shared pointer) is first created and data are " "loaded from an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 352); REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 7); AND_GIVEN( "The allocation of CleavageConfig (monoprotonation) and an Ionizer, and " "a " "Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 1); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::CROSS_LINKER, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 351); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { qDebug() << "Right before cleavage."; REQUIRE(cleaver.cleave(/*reset*/ false)); qDebug() << "Right after cleavage."; THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 32); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-2]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(306.1599858302, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(306.4063019185, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C11H24N5O3S1"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "cl-0#31+0#34+0#36#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[294-297][312-326][332-334]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(2390.0262359943, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(2391.7207845530, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C16H30N7O5S1"); } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) and an Ionizer, " "and a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::CROSS_LINKER, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 351); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 96); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-2]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(306.1599858302, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(306.4063019185, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C11H24N5O3S1"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "cl-0#31+0#34+0#36#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[294-297][312-326][332-334]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(797.3472953530, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(797.9122224966, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C16H32N7O5S1"); } } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) partials = 2 and " "an Ionizer, and a Cleaver object") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setPartials(2); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::CROSS_LINKER, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 351); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 315); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-2]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(306.1599858302, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(306.4063019185, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C11H24N5O3S1"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#36#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[332-352]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(796.6797617835, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(797.1940963187, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C99H153N28O37S2"); } } } AND_GIVEN( "The allocation of CleavageConfig (protonation 1->3) partials = 2 and " "an Ionizer, and a Cleaver object and one shortened sequence range") { CleavageConfig m_cleavage_config( polymer_sp->getPolChemDefCstSPtr(), "Trypsin", "K/;R/;-K/P", 0, true); m_cleavage_config.setIonizeLevels(1, 3); m_cleavage_config.setPartials(2); m_cleavage_config.setSequenceEmbedded(true); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::CROSS_LINKER, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 187); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Cleaver cleaver(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), m_cleavage_config, calc_options, protonation); WHEN("The cleavage is asked for") { REQUIRE(cleaver.cleave(/*reset*/ false)); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(cleaver.getOligomerCollectionCstRef().size() == 186); OligomerSPtr oligomer_sp = cleaver.getOligomerCollectionCstRef() .getOligomersCstRef() .front(); REQUIRE(oligomer_sp->getName().toStdString() == "0#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[1-2]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(306.1599858302, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(306.4063019185, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C11H24N5O3S1"); oligomer_sp = cleaver.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "2#20#z=3"); REQUIRE(oligomer_sp->getCalcOptionsRef() .getIndexRangeCollectionRef() .positionsAsText() .toStdString() == "[159-188]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "Trypsin"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1099.5362628891, 0.0000000001)); REQUIRE_THAT( oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1100.2372770376, 0.0000000001)); REQUIRE(oligomer_sp->getFormulaCstRef() .getActionFormula() .toStdString() == "C142H229N38O48S2"); } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CoordinateList.cpp000664 001750 001750 00000053337 15100504560 023300 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("Construction of a SequenceRanges instance", "[SequenceRanges]") { QString sequence_range_string_1("[50-150]"); SequenceRange sequence_range_1(50, 150); QString sequence_range_string_2("[40-160]"); SequenceRange sequence_range_2(40, 160); QString sequence_range_string_3("[30-170]"); SequenceRange sequence_range_3(30, 170); QString all_three_sequence_range_strings = QString("%1%2%3") .arg(sequence_range_string_1) .arg(sequence_range_string_2) .arg(sequence_range_string_3); GIVEN("Construction with no argument") { SequenceRanges sequence_ranges; WHEN("A comment is set to it") { sequence_ranges.setComment("Comment"); THEN("The comment should be set") { REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); } } } GIVEN( "Construction of a SequenceRanges instance with a string representing " "three SequenceRange instances") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list must have size 3") { REQUIRE(sequence_ranges.size() == 3); AND_THEN("All the members of the list should have proper values") { SequenceRange first_sequence_range = sequence_ranges.getCstReferenceAt(0); REQUIRE(first_sequence_range.first == sequence_range_1.first - 1); REQUIRE(first_sequence_range.second == sequence_range_1.second - 1); SequenceRange second_sequence_range = sequence_ranges.getCstReferenceAt(1); REQUIRE(second_sequence_range.first == sequence_range_2.first - 1); REQUIRE(second_sequence_range.second == sequence_range_2.second - 1); SequenceRange third_sequence_range = sequence_ranges.getCstReferenceAt(2); REQUIRE(third_sequence_range.first == sequence_range_3.first - 1); REQUIRE(third_sequence_range.second == sequence_range_3.second - 1); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "'[')") { QString all_three_sequence_range_strings("50-150][40-160][30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "']'") { QString all_three_sequence_range_strings("[50-150][40-160[30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "']' and 1 '['") { QString all_three_sequence_range_strings("50-150][40-160[30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "'-'") { QString all_three_sequence_range_strings("[50-150][40-160][30 170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN("Construction with a comment and no list; and SequenceRange objects") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Comment"); REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); REQUIRE(sequence_ranges.size() == 0); WHEN("One SequenceRange object is appended to the list") { sequence_ranges.appendSequenceRange(sequence_range_1); THEN( "The list must have size 1, and that object has to be the one appended") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getCstReferenceAt(0) == sequence_range_1); } AND_WHEN("Another SequenceRange object is appended to the list") { sequence_ranges.appendSequenceRange(sequence_range_2); THEN( "The list must have size 2, and that object has to be the last of " "the list") { REQUIRE(sequence_ranges.size() == 2); REQUIRE(sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1) == sequence_range_2); } AND_WHEN( "That 2-sized SequenceRanges instance is copied (copy-constructor " "with reference)") { SequenceRanges new_sequence_ranges(sequence_ranges); THEN("The two SequenceRanges instances must have the same data") { REQUIRE(new_sequence_ranges.getComment().toStdString() == sequence_ranges.getComment().toStdString()); REQUIRE(new_sequence_ranges.size() == sequence_ranges.size()); REQUIRE(new_sequence_ranges.getCstReferenceAt(0) == sequence_ranges.getCstReferenceAt(0)); REQUIRE(new_sequence_ranges.getCstReferenceAt( new_sequence_ranges.size() - 1) == sequence_ranges.getCstReferenceAt( new_sequence_ranges.size() - 1)); } } AND_WHEN( "That 2-sized SequenceRanges instance is copied (assignment " "operator) to itself, a reference to itself is returned") { REQUIRE((sequence_ranges = sequence_ranges) == sequence_ranges); } AND_WHEN( "That 2-sized SequenceRanges instance is copied (assignment " "operator)") { SequenceRanges new_sequence_ranges; new_sequence_ranges = sequence_ranges; THEN("The two SequenceRanges instances must have the same data") { REQUIRE(new_sequence_ranges.getComment().toStdString() == sequence_ranges.getComment().toStdString()); REQUIRE(new_sequence_ranges.size() == sequence_ranges.size()); REQUIRE(new_sequence_ranges.getCstReferenceAt(0) == sequence_ranges.getCstReferenceAt(0)); REQUIRE( new_sequence_ranges.getCstReferenceAt(new_sequence_ranges.size() - 1) == sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1)); } } } } } } SCENARIO( "Empty SequenceRanges instances can be filled-in after construction with " "other SequenceRanges", "[SequenceRanges]") { GIVEN( "Construction of a SequenceRanges instance with two SequenceRange " "instances") { SequenceRange sequence_range_1(50, 150); SequenceRange sequence_range_2(40, 160); SequenceRange sequence_range_3(30, 170); SequenceRanges sequence_ranges_0; sequence_ranges_0.setComment("Giver"); sequence_ranges_0.appendSequenceRange(sequence_range_1); sequence_ranges_0.appendSequenceRange(sequence_range_2); REQUIRE(sequence_ranges_0.size() == 2); REQUIRE(sequence_ranges_0.getCstReferenceAt(0) == sequence_range_1); REQUIRE(sequence_ranges_0.getCstReferenceAt(sequence_ranges_0.size() - 1) == sequence_range_2); AND_GIVEN("Construction of an empty SequenceRanges instance") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Receiver"); REQUIRE(sequence_ranges.getComment().toStdString() == "Receiver"); REQUIRE(sequence_ranges.size() == 0); WHEN( "The first SequenceRanges instance with two items is appended to the " "the empty one") { sequence_ranges.appendSequenceRanges(sequence_ranges_0); THEN("Its size must become the same at the one that was appended") { REQUIRE(sequence_ranges.size() == sequence_ranges_0.size()); REQUIRE(sequence_ranges.getCstReferenceAt(0) == sequence_ranges_0.getCstReferenceAt(0)); REQUIRE( sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1) == sequence_ranges_0.getCstReferenceAt(sequence_ranges_0.size() - 1)); } AND_WHEN("A new SequenceRange instance is set (not appended)") { sequence_ranges.setSequenceRange(sequence_range_3); THEN("The single instance has to be this last") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getCstReferenceAt(0) == sequence_range_3); } AND_WHEN("A SequenceRanges instance is set (not appended)") { sequence_ranges.setSequenceRanges(sequence_ranges_0); THEN( "The previous single instance has to be lost and be replaced by " "the new ones") { REQUIRE(sequence_ranges.size() == 2); REQUIRE(sequence_ranges.getCstReferenceAt(0) == sequence_ranges_0.getCstReferenceAt(0)); REQUIRE( sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1) == sequence_ranges_0.getCstReferenceAt(sequence_ranges_0.size() - 1)); } } } } } } SCENARIO( "Empty SequenceRanges instances can be filled in after construction with " "strings representative of SequenceRange instances (single or multiple)", "[SequenceRanges]") { QString sequence_range_string_1("[50-150]"); SequenceRange sequence_range_1(50, 150); QString sequence_range_string_2("[40-160]"); SequenceRange sequence_range_2(40, 160); QString sequence_range_string_3("[30-170]"); SequenceRange sequence_range_3(30, 170); QString all_three_sequence_range_strings = QString("%1%2%3") .arg(sequence_range_string_1) .arg(sequence_range_string_2) .arg(sequence_range_string_3); GIVEN("Construction with a comment and no list; and SequenceRange objects") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Comment"); REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); REQUIRE(sequence_ranges.size() == 0); WHEN( "That SequenceRanges is set with one string representative of a single " "SequenceRange instance") { sequence_ranges.setSequenceRanges(sequence_range_string_1); THEN( "The list must have size 1, and that object has to be the one set, " "with each first/second value decremented by one to convert " "positions to " "indices") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getCstReferenceAt(0).first == sequence_range_1.first - 1); REQUIRE(sequence_ranges.getCstReferenceAt(0).second == sequence_range_1.second - 1); } } WHEN( "That SequenceRanges is set with one string representative of three " "SequenceRange instances") { sequence_ranges.setSequenceRanges(all_three_sequence_range_strings); THEN("The list must have size 3") { REQUIRE(sequence_ranges.size() == 3); } AND_THEN("All the members of the list should have proper values") { REQUIRE(sequence_ranges.getCstReferenceAt(0).first == sequence_range_1.first - 1); REQUIRE(sequence_ranges.getCstReferenceAt(0).second == sequence_range_1.second - 1); REQUIRE(sequence_ranges.getCstReferenceAt(1).first == sequence_range_2.first - 1); REQUIRE(sequence_ranges.getCstReferenceAt(1).second == sequence_range_2.second - 1); REQUIRE(sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1) .first == sequence_range_3.first - 1); REQUIRE(sequence_ranges.getCstReferenceAt(sequence_ranges.size() - 1) .second == sequence_range_3.second - 1); } } } } SCENARIO("SequenceRanges instances can provide topological data", "[SequenceRanges]") { GIVEN("A SequenceRanges instance with a single SequenceRange instance") { SequenceRanges sequence_ranges; QString all_sequence_range_strings("[1-20]"); sequence_ranges.setSequenceRanges(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 1); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRange") { REQUIRE(sequence_ranges.leftMostSequenceRange(left_most_indices) == 1); REQUIRE(sequence_ranges.rightMostSequenceRange(right_most_indices) == 1); THEN( "The indices should actually match the correct SequenceRange " "instance") { REQUIRE( sequence_ranges.getCstReferenceAt(left_most_indices.at(0)).first == 0); REQUIRE(sequence_ranges.getCstReferenceAt(right_most_indices.at(0)) .second == 19); } } WHEN( "Checking if there are overlaps, there cannot be because only one " "instance") { REQUIRE_FALSE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE(sequence_ranges.encompassIndex(19)); REQUIRE(sequence_ranges.encompassIndex(10)); REQUIRE_FALSE(sequence_ranges.encompassIndex(20)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); } } } GIVEN( "A SequenceRanges instance with a number of non-overlapping " "SequenceRange") { SequenceRanges sequence_ranges; QString all_sequence_range_strings("[1-20][25-45][55-100]"); sequence_ranges.setSequenceRange(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 3); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRange") { REQUIRE(sequence_ranges.leftMostSequenceRange(left_most_indices) == 1); REQUIRE(sequence_ranges.rightMostSequenceRange(right_most_indices) == 1); THEN( "The indices should actually match the right SequenceRange instances") { REQUIRE( sequence_ranges.getCstReferenceAt(left_most_indices.at(0)).first == 0); REQUIRE(sequence_ranges.getCstReferenceAt(right_most_indices.at(0)) .second == 99); } } WHEN("Checking if there are overlaps, there should not be") { REQUIRE_FALSE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE_FALSE(sequence_ranges.encompassIndex(50)); REQUIRE(sequence_ranges.encompassIndex(50, /*globally*/ true)); REQUIRE(sequence_ranges.encompassIndex(99)); REQUIRE_FALSE(sequence_ranges.encompassIndex(100)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { // "[1-20][25-45][55-100]" REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(1))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(1))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(2))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(2))); } } } GIVEN( "A SequenceRanges instance with a number of SequenceRange with overlaps") { SequenceRanges sequence_ranges; QString all_sequence_range_strings( "[1-20][1-30][10-50][15-80][25-100][30-100]"); sequence_ranges.setSequenceRange(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 6); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRange") { REQUIRE(sequence_ranges.leftMostSequenceRange(left_most_indices) == 2); REQUIRE(sequence_ranges.rightMostSequenceRange(right_most_indices) == 2); THEN( "The indices should actually match the right SequenceRange instances") { REQUIRE( sequence_ranges.getCstReferenceAt(left_most_indices.at(0)).first == 0); REQUIRE( sequence_ranges.getCstReferenceAt(left_most_indices.back()).first == 0); REQUIRE(sequence_ranges.getCstReferenceAt(right_most_indices.at(0)) .second == 99); REQUIRE(sequence_ranges.getCstReferenceAt(right_most_indices.back()) .second == 99); } } WHEN("Checking if there are overlaps, there should be") { REQUIRE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE(sequence_ranges.encompassIndex(49)); REQUIRE(sequence_ranges.encompassIndex(50)); REQUIRE(sequence_ranges.encompassIndex(99)); REQUIRE_FALSE(sequence_ranges.encompassIndex(100)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { // "[1-20][1-30][10-50][15-80][25-100][30-100]" REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(0))); REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(1))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(1))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(2))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(2))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(3))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(3))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(4))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(4))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getCstReferenceAt(5))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getCstReferenceAt(5))); } } } } SCENARIO("SequenceRanges instances can self-describe with text strings", "[SequenceRanges]") { QString all_three_sequence_range_strings("[50-150][40-160][30-170]"); GIVEN("Construction with a string describing three SequenceRange instances") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); WHEN("When asked to self-describe as indices") { QString text = sequence_ranges.indicesAsText(); THEN("The string should be list indices and not positions") { REQUIRE(text.toStdString() == "[49-149][39-159][29-169]"); } } WHEN("When asked to self-describe as positions") { QString text = sequence_ranges.positionsAsText(); THEN("The string should be list positions and not indices") { REQUIRE(text.toStdString() == all_three_sequence_range_strings.toStdString()); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CrossLink.cpp000664 001750 001750 00000070756 15100504560 022270 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_cross_link("protein-1-letter", 1); ErrorList error_list_cross_link; SCENARIO("Construction of a CrossLink", "[CrossLink]") { test_utils_1_letter_cross_link.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_link.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("cyan-fluorescent-protein-no-cross-link.mxp"); GIVEN( "A Polymer is allocated as a SPtr and then the XML file is loaded from " "disk") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 240); WHEN( "A CrossLink is allocated by specifying all the params in the " "constructor") { CrossLink cross_link(pol_chem_def_csp, polymer_sp, "CFP-chromophore", "-H2OH2", "This is a comment"); THEN("The data in the cross-link must be set correctly") { REQUIRE(cross_link.getPolymerCstSPtr() == polymer_sp); REQUIRE(cross_link.getComment().toStdString() == "This is a comment"); REQUIRE(cross_link.getCrossLinkerName().toStdString() == "CFP-chromophore"); REQUIRE( cross_link.getCrossLinkerCstSPtr()->getFormula().toStdString() == "-H2OH2"); } AND_WHEN( "Another CrossLink is allocated by copy-construction or assignment") { CrossLink another_cross_link(cross_link); CrossLink other_cross_link; other_cross_link = another_cross_link; THEN("The new CrossLink instance have to be identical to the first one") { REQUIRE(other_cross_link.getPolymerCstSPtr() == polymer_sp); REQUIRE(other_cross_link.getComment().toStdString() == "This is a comment"); REQUIRE(other_cross_link.getCrossLinkerName().toStdString() == "CFP-chromophore"); REQUIRE(other_cross_link.getCrossLinkerCstSPtr() ->getFormula() .toStdString() == "-H2OH2"); } } } } } SCENARIO("Construction of a CrossLink and use with a Polymer", "[CrossLink]") { test_utils_1_letter_cross_link.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_link.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("cyan-fluorescent-protein-no-cross-link.mxp"); GIVEN( "A Polymer is allocated as a SPtr and then the XML file is loaded from " "disk") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 240); WHEN("A CrossLinker is referenced from the PolChemDef by name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(cross_linker_csp != nullptr); REQUIRE(cross_linker_csp->getPolChemDefCstSPtr() == pol_chem_def_csp); THEN( "Its masses have to be ready because the CrossLinker is in the " "PolChemDef") { REQUIRE_THAT(cross_linker_csp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT(cross_linker_csp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); double mono = 1000; double avg = 1001; cross_linker_csp->accountMasses(mono, avg, 3); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(1000 - (20.0262147493 * 3), 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(1001 - (20.0311745907 * 3), 0.0000000001)); } WHEN( "A CrossLink instance is created using that CrossLinker instance, " "the " "CSPtr is gotten from the raw pointer") { CrossLinkSPtr cross_link_sp = std::make_shared(cross_linker_csp, polymer_sp->getCstSharedPtr(), "This is the CFP-chromophore cross-link"); AND_WHEN( "The cross-link is actually created by adding Monomer instances") { bool ok = false; // Failing addition: cross_link_sp->fillInMonomers("", ok); REQUIRE_FALSE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 0); // And now the real one cross_link_sp->fillInMonomers(";66;67;", ok); THEN("The Monomer instances are correctly set") { REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); REQUIRE(cross_link_sp ->continuumOfLocationsOfInclusiveSequenceMonomersAsText( Enums::LocationType::INDEX) .toStdString() == ";66;67;"); } AND_WHEN("Another Monomer instance is set also") { MonomerSPtr third_monomer_sp = polymer_sp->getSequenceCstRef().getMonomerCstSPtrAt(68); cross_link_sp->appendMonomer(third_monomer_sp); THEN("The new Monomer instance is set correctly and all are there") { REQUIRE(cross_link_sp->getMonomersCstRef().size() == 3); REQUIRE(cross_link_sp->getMonomersRef().size() == 3); REQUIRE(cross_link_sp ->continuumOfLocationsOfInclusiveSequenceMonomersAsText( Enums::LocationType::INDEX) .toStdString() == ";66;67;68;"); std::vector extreme_locations = cross_link_sp->locationssOfOnlyExtremeSequenceMonomers( Enums::LocationType::INDEX); REQUIRE(extreme_locations.front() == 66); REQUIRE(extreme_locations.back() == 68); REQUIRE(cross_link_sp ->locationsOfOnlyExtremeSequenceMonomersAsText( Enums::LocationType::INDEX) .toStdString() == ";66;68;"); MonomerCstSPtr first_monomer_csp = cross_link_sp->getMonomerAt(0); REQUIRE(cross_link_sp->getFirstMonomer() == first_monomer_csp); REQUIRE(cross_link_sp->monomerIndex(first_monomer_csp, ok) == 0); REQUIRE( cross_link_sp->monomerIndex(first_monomer_csp.get(), ok) == 0); REQUIRE(ok); MonomerCstSPtr second_monomer_csp = cross_link_sp->getMonomerAt(1); REQUIRE(cross_link_sp->monomerIndex(second_monomer_csp, ok) == 1); REQUIRE( cross_link_sp->monomerIndex(second_monomer_csp.get(), ok) == 1); REQUIRE(ok); REQUIRE(polymer_sp->getSequenceCstRef().monomerIndex( first_monomer_csp, ok) == 66); REQUIRE(ok); REQUIRE(polymer_sp->getSequenceCstRef().monomerIndex( second_monomer_csp, ok) == 67); REQUIRE(ok); AND_THEN("Removing and readding the last Monomer should work") { cross_link_sp->removeMonomerAt(2); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); cross_link_sp->appendMonomer(third_monomer_sp); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 3); } THEN("The mass of the CrossLink can be computed") { REQUIRE_THAT( cross_link_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT( cross_link_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); double mono = 1000; double avg = 1001; cross_linker_csp->accountMasses(mono, avg, 3); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs( 1000 - (20.0262147493 * 3), 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs( 1001 - (20.0311745907 * 3), 0.0000000001)); AND_WHEN( "The CrossLink is actually performed onto the Polymer's " "Monomer " "instances") { polymer_sp->crossLink(cross_link_sp); THEN("The CrossLink must be found in the Polymer") { REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 1); } AND_WHEN( "Configuring calculation options NOT to account for " "cross-links") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 240 - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 1); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs( 26885.5345715247, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs( 26902.1945837392, 0.0000000001)); } AND_WHEN( "Configuring calculation options to account for " "cross-links") { REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 1); CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 240 - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 1); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs( 26865.5083567754, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs( 26882.1634091485, 0.0000000001)); } } } } } } } } } } } } } } SCENARIO( "Construction of a set of 7 CrossLink instances and use with a pristine " "Polymer", "[CrossLink]") { test_utils_1_letter_cross_link.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_link.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("kunitz-inhibitor-human-no-cross-link.mxp"); GIVEN("A Polymer instance created by loading an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 352); AND_GIVEN("An allocated CrossLinker as found in the PolChemDef by name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("DisulfideBond"); REQUIRE(cross_linker_csp != nullptr); REQUIRE(cross_linker_csp->getPolChemDefCstSPtr() == pol_chem_def_csp); // Now allocated seven "DisulfideBond" CrossLinks // THESE ARE POSITIONS, NOT INDICES // DisulfideBond/;91;188 // DisulfideBond/;231;281 // DisulfideBond/;240;264 // DisulfideBond/;256;277 // DisulfideBond/;287;337 // DisulfideBond/;296;320 // DisulfideBond/;312;333 bool ok = false; CrossLinkSPtr cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";90;187;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";230;280;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";239;263;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";255;276;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";286;336;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";295;319;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); polymer_sp->crossLink(cross_link_sp); cross_link_sp = std::make_shared( cross_linker_csp, polymer_sp->getCstSharedPtr(), "This one of the DisulfideBond cross-links"); cross_link_sp->fillInMonomers(";311;332;", ok); REQUIRE(ok); REQUIRE(cross_link_sp->getMonomersCstRef().size() == 2); MonomerCstSPtr monomer_csp = polymer_sp->getSequenceCstRef().getMonomerCstSPtrAt(311); REQUIRE(ok); REQUIRE(monomer_csp != nullptr); REQUIRE(cross_link_sp->hasUuid(monomer_csp)); QString uuid = cross_link_sp->getUuidForMonomer(monomer_csp); MonomerCstSPtr same_monomer_csp = cross_link_sp->getMonomerForUuid(uuid); REQUIRE(same_monomer_csp == monomer_csp); std::vector all_monomer_uuids = cross_link_sp->getAllMonomerUuids(); REQUIRE(all_monomer_uuids.size() == 2); polymer_sp->crossLink(cross_link_sp); WHEN("The polymer has been cross-linked with the CrossLink instances") { THEN("The CrossLink instances must be found in the Polymer") { REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 7); std::size_t in_count; std::size_t out_count; AND_THEN("Encompassing logic can be tested") { // THESE ARE POSITIONS, NOT INDICES // DisulfideBond/;91;188 // DisulfideBond/;231;281 // DisulfideBond/;240;264 // DisulfideBond/;256;277 // DisulfideBond/;287;337 // DisulfideBond/;296;320 // DisulfideBond/;312;333 std::vector monomer_indices = polymer_sp->getCrossLinksCstRef() .at(0) ->continuumOfLocationsOfInclusiveSequenceMonomers( Enums::LocationType::INDEX); REQUIRE(monomer_indices.size() == 188 - 91 + 1); REQUIRE(monomer_indices.at(0) == 90); REQUIRE(monomer_indices.at(97) == 187); REQUIRE(polymer_sp->getCrossLinksCstRef().at(0) != nullptr); REQUIRE( polymer_sp->getCrossLinksCstRef() .at(0) ->isEncompassedByIndexRange(90, 187, in_count, out_count) == Enums::CrossLinkEncompassed::FULLY); REQUIRE( polymer_sp->getCrossLinksCstRef() .at(0) ->isEncompassedByIndexRange(91, 187, in_count, out_count) == Enums::CrossLinkEncompassed::PARTIALLY); REQUIRE( polymer_sp->getCrossLinksCstRef() .at(0) ->isEncompassedByIndexRange(90, 186, in_count, out_count) == Enums::CrossLinkEncompassed::PARTIALLY); REQUIRE( polymer_sp->getCrossLinksCstRef() .at(0) ->isEncompassedByIndexRange(92, 187, in_count, out_count) == Enums::CrossLinkEncompassed::PARTIALLY); REQUIRE( polymer_sp->getCrossLinksCstRef() .at(0) ->isEncompassedByIndexRange(92, 186, in_count, out_count) == Enums::CrossLinkEncompassed::NOT); } THEN( "The copy-constructor and assignment-based CrossLink creation can " "be tested thoroughly") { CrossLink another_cross_link(*cross_link_sp); CrossLink other_cross_link; other_cross_link = another_cross_link; REQUIRE(other_cross_link == *cross_link_sp); REQUIRE_FALSE(other_cross_link != *cross_link_sp); REQUIRE(other_cross_link.isValid()); double mono = 0; double avg = 0; REQUIRE(other_cross_link.calculateMasses( pol_chem_def_csp->getIsotopicDataCstSPtr(), mono, avg)); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(-2.01565006453, 0.0000000001)); QString expected_text = "\nCross-link:\n===============\nCross-linker name: " "DisulfideBond\nCross-link comment: This one of the " "DisulfideBond cross-links\nPartner 1: C at position: " "312\nPartner 2: C at position: 333\n"; REQUIRE(cross_link_sp->prepareResultsTxtString().toStdString() == expected_text.toStdString()); } } AND_WHEN( "Configuring calculation options NOT to account for cross-links") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, polymer_sp->size() - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(38973.9813242497, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(38999.3128988044, 0.0000000001)); } AND_WHEN( "Configuring calculation options to account for cross-links") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, polymer_sp->size() - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(38959.8717737979, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(38985.2017182470, 0.0000000001)); } } WHEN( "Configuring calculation options to account for " "cross-links " "with only one fully encompassed") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(55, 216)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(18241.1791940081, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(18252.6111982139, 0.0000000001)); } } } WHEN( "Configuring calculation options to account for " "cross-links " "with more than one fully encompassed") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(72, 284)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(23696.2700807047, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(23711.5168000099, 0.0000000001)); } } WHEN( "Configuring calculation options to account for " "cross-links " "with more than one fully encompassed") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(196, 283)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(9571.3824122666, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(9577.7946954897, 0.0000000001)); } } } WHEN( "Configuring calculation options to account for " "cross-links " "with two incomplete cross-links") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(155, 286)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(14356.6660883098, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(14366.1307048399, 0.0000000001)); } } } } } } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CrossLinkedRegion.cpp000664 001750 001750 00000012636 15100504560 023736 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_cross_linked_region("protein-1-letter", 1); ErrorList error_list_cross_linked_region; SCENARIO("Construction of an empty CrossLinkedRegion", "[CrossLinkedRegion]") { CrossLinkedRegion cross_linked_region; } SCENARIO("Construction of a CrossLinkedRegion with indices", "[CrossLinkedRegion]") { WHEN("Allocating a CrossLinkedRegion with indices") { CrossLinkedRegion cross_linked_region(100, 120); THEN("The member data are set accordingly") { REQUIRE(cross_linked_region.getStartIndex() == 100); REQUIRE(cross_linked_region.getStopIndex() == 120); REQUIRE(cross_linked_region.getCrossLinksRef().size() == 0); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 0); } } } SCENARIO( "Construction of an empty CrossLinkedRegion and setting indices with setters", "[CrossLinkedRegion]") { WHEN("Allocating a CrossLinkedRegion with indices") { CrossLinkedRegion cross_linked_region; cross_linked_region.setStartIndex(100); cross_linked_region.setStopIndex(120); THEN("The member data are set accordingly") { REQUIRE(cross_linked_region.getStartIndex() == 100); REQUIRE(cross_linked_region.getStopIndex() == 120); REQUIRE(cross_linked_region.getCrossLinksRef().size() == 0); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 0); } } } SCENARIO( "Construction of an empty CrossLinkedRegion and adding CrossLink instances", "[CrossLinkedRegion]") { GIVEN("CrossLink instances") { test_utils_1_letter_cross_linked_region.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linked_region.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("cyan-fluorescent-protein-no-cross-link.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 240); CrossLinkSPtr cross_link_1_sp = std::make_shared(pol_chem_def_csp, polymer_sp, "CFP-chromophore", "-H2OH2", "This is a comment for cross-link 1"); CrossLinkSPtr cross_link_2_sp = std::make_shared(pol_chem_def_csp, polymer_sp, "CFP-chromophore", "-H2OH2", "This is a comment for cross-link 2"); CrossLinkSPtr cross_link_3_sp = std::make_shared(pol_chem_def_csp, polymer_sp, "CFP-chromophore", "-H2OH2", "This is a comment for cross-link 3"); std::vector cross_links; cross_links.push_back(cross_link_2_sp); cross_links.push_back(cross_link_3_sp); AND_GIVEN("A CrossLinkedRegion allocated with indices") { CrossLinkedRegion cross_linked_region(100, 120); THEN("The member data are set accordingly") { REQUIRE(cross_linked_region.getStartIndex() == 100); REQUIRE(cross_linked_region.getStopIndex() == 120); REQUIRE(cross_linked_region.getCrossLinksRef().size() == 0); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 0); } WHEN( "New CrossLink instances are added, these are stored in the " "CrossLinkedRegion") { cross_linked_region.appendCrossLink(cross_link_1_sp); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 1); cross_linked_region.appendCrossLinks(cross_links); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 3); AND_WHEN( "CrossLink instances are remove, these disappear from the " "CrossLinkedRegion") { cross_linked_region.removeCrossLink(cross_link_1_sp); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 2); REQUIRE(cross_linked_region.getCrossLinksCstRef() .front() ->getComment() .toStdString() == "This is a comment for cross-link 2"); cross_linked_region.removeCrossLinkAt(0); REQUIRE(cross_linked_region.getCrossLinksCstRef().size() == 1); REQUIRE(cross_linked_region.getCrossLinksCstRef() .front() ->getComment() .toStdString() == "This is a comment for cross-link 3"); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_CrossLinker.cpp000664 001750 001750 00000103351 15100504560 022603 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_cross_linker("protein-1-letter", 1); ErrorList error_list_cross_linker; SCENARIO("Construction of a CrossLinker with a proper XML element", "[CrossLinker]") { // // CFP-chromophore // // Chromo-O // Chromo-H3 // Chromo-H // QStringList dom_strings{"clk", "name", "CFP-chromophore", "formula", "", "modifname", "Chromo-O", "modifname", "Chromo-H3", "modifname", "Chromo-H"}; PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN( "A CrossLinker is constructed with PolChemDef and a valid XML clk element") { QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n Chromo-H3\n " "Chromo-H\n\n"); CrossLinker cross_linker(pol_chem_def_csp, clk_element, /*version*/ 1); THEN( "It is valid, does validate successfully and has all proper member data") { REQUIRE(cross_linker.getPolChemDefCstSPtr() == pol_chem_def_csp); error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == ""); REQUIRE(cross_linker.getModifsCstRef().size() == 3); REQUIRE(cross_linker.getModifsRef().size() == 3); REQUIRE( (*cross_linker.getModifsCstRef().cbegin())->getName().toStdString() == "Chromo-O"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 1)) ->getName() .toStdString() == "Chromo-H3"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 2)) ->getName() .toStdString() == "Chromo-H"); } } } SCENARIO( "Construction of a CrossLinker with nullptr PolChemDefCstSPtr and a proper " "XML element", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; QStringList dom_strings{"clk", "name", "CFP-chromophore", "formula", "", "modifname", "Chromo-O", "modifname", "Chromo-H3", "modifname", "Chromo-H"}; QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n Chromo-H3\n " "Chromo-H\n\n"); WHEN( "Allocating a CrossLinker with null PolChemDefCstSPtr and a valid XML " "element") { CrossLinker cross_linker(nullptr, clk_element); THEN("The CrossLinker fails to validate.") { REQUIRE_FALSE(cross_linker.isValid()); } } } SCENARIO( "Construction of a CrossLinker with proper PolChemDefCstSPtr and a proper " "XML element", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; QStringList dom_strings{"clk", "name", "CFP-chromophore", "formula", "", "modifname", "Chromo-O", "modifname", "Chromo-H3", "modifname", "Chromo-H"}; // Failing tag dom_strings.replace(0, "erroneous_clk_tag"); QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n " "\n " "Chromo-O\n Chromo-H3\n " "Chromo-H\n\n"); WHEN( "Allocating a CrossLinker with null PolChemDefCstSPtr and a valid XML " "element") { CrossLinker cross_linker(pol_chem_def_csp, clk_element); THEN("The CrossLinker fails to validate.") { REQUIRE_FALSE(cross_linker.isValid()); } } } SCENARIO( "Construction of a CrossLinker lacking PolChemDefCstSPtr or not with a " "proper XML element", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; QStringList dom_strings{"clk", "name", "CFP-chromophore", "formula", "", "modifname", "Chromo-O", "modifname", "Chromo-H3", "modifname", "Chromo-H"}; WHEN("A CrossLinker is created empty") { CrossLinker cross_linker; THEN("It is invalid and does not validate successfully") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } AND_WHEN( "The CrossLinker lacking PolChemDefCstSPtr is rendered as an XML " "element") { QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE(document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); REQUIRE_FALSE( cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); THEN("Some data are set, other not, the CrossLinker is not valid") { REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == ""); REQUIRE(cross_linker.getModifsCstRef().size() == 0); error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } } } WHEN( "A CrossLinker is created empty and then the PolChemDef is set with the " "setter") { CrossLinker cross_linker; cross_linker.setPolChemDefCstSPtr(pol_chem_def_csp); THEN("It is invalid and does not validate successfully") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } AND_WHEN("The CrossLinker is rendered as an XML element") { QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE(document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); REQUIRE(cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); THEN( "It is valid, does validate successfully and has all proper member " "data") { error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == ""); REQUIRE(cross_linker.getModifsCstRef().size() == 3); REQUIRE( (*cross_linker.getModifsCstRef().cbegin())->getName().toStdString() == "Chromo-O"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 1)) ->getName() .toStdString() == "Chromo-H3"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 2)) ->getName() .toStdString() == "Chromo-H"); } } } } SCENARIO( "Construction of a CrossLinker with PolChemDefCstSPtr but with a " "failing XML element", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; QStringList dom_strings{"clk", "name", "CFP-chromophore", "formula", "", "modifname", "Chromo-O", "modifname", "Chromo-H3", "modifname", "Chromo-H"}; GIVEN("An empty CrossLinker set with a proper PolChemDefCstSPtr") { CrossLinker cross_linker; cross_linker.setPolChemDefCstSPtr(pol_chem_def_csp); THEN("It is invalid and does not validate successfully") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } AND_WHEN("The CrossLinker is rendered as a failing XML element tag") { // Failing tag. dom_strings.replace(0, "clw"); QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE(document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); REQUIRE_FALSE( cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); THEN("It is not valid because the XML element is failing") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } } AND_WHEN("The CrossLinker is rendered as a failing XML element tag") { dom_strings.replace(0, "clk"); dom_strings.replace(1, "erroneous_name_tag"); QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n " "\n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); REQUIRE_FALSE( cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); THEN("It is not valid because the XML element is failing") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } } AND_WHEN( "The CrossLinker is rendered as a failing XML element tag") { dom_strings.replace(1, "name"); dom_strings.replace(3, "erroneous_formula_tag"); QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE(document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n " "\n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); THEN("It is not valid because the XML element is failing") { REQUIRE_FALSE( cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); } } AND_WHEN( "The CrossLinker is rendered as a failing XML element text") { dom_strings.replace(3, "formula"); dom_strings.replace(4, "+HWKPTR"); QDomDocument document = test_utils_1_letter_cross_linker.craftClkDomDocument(dom_strings); QDomElement clk_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n CFP-chromophore\n +HWKPTR\n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); REQUIRE_FALSE( cross_linker.renderXmlClkElement(clk_element, /*version*/ 1)); } } } SCENARIO("Construction of a CrossLinker starting from empty with the setters", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created empty") { CrossLinker cross_linker; THEN("It is invalid and does not validate successfully") { error_list_cross_linker.clear(); REQUIRE_FALSE(cross_linker.validate(error_list_cross_linker)); REQUIRE_FALSE(cross_linker.isValid()); } AND_WHEN("The CrossLinker is configured piecemeal with setters") { cross_linker.setPolChemDefCstSPtr(pol_chem_def_csp); cross_linker.setName("CFP-chromophore"); cross_linker.setFormula(""); // Get Modifs from the PolChemDef. ModifCstSPtr modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-O"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H3"); REQUIRE(modif_csp != nullptr); cross_linker.insertModifAt(modif_csp, 1); THEN( "It is valid, does validate successfully and has all proper member " "data") { error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == ""); REQUIRE(cross_linker.getModifsCstRef().size() == 3); REQUIRE( (*cross_linker.getModifsCstRef().cbegin())->getName().toStdString() == "Chromo-O"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 1)) ->getName() .toStdString() == "Chromo-H3"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 2)) ->getName() .toStdString() == "Chromo-H"); REQUIRE(cross_linker.getModifAt(0) == (*cross_linker.getModifsCstRef().cbegin())); REQUIRE(cross_linker.getModifAt(2) == (*(std::prev(cross_linker.getModifsCstRef().cend())))); AND_THEN("The masses should be calculated fine") { REQUIRE(cross_linker.calculateMasses(nullptr)); REQUIRE_THAT( cross_linker.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT( cross_linker.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); } } } } } SCENARIO("CrossLinker instances can be set and removed Modif instances", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created with name and formula but not Modif") { CrossLinker cross_linker(pol_chem_def_csp, "CFP-chromophore", "-H2O"); THEN( "It is valid, does validate successfully and has all proper member " "data") { error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == "-H2O"); REQUIRE(cross_linker.getModifsCstRef().size() == 0); } AND_WHEN("Modifications are added to it") { // Get Modifs from the PolChemDef. ModifCstSPtr modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-O"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H3"); REQUIRE(modif_csp != nullptr); cross_linker.insertModifAt(modif_csp, 1); THEN( "These are now available in the CrossLinker and can be checked fully") { REQUIRE(cross_linker.getModifsCstRef().size() == 3); REQUIRE( (*cross_linker.getModifsCstRef().cbegin())->getName().toStdString() == "Chromo-O"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 1)) ->getName() .toStdString() == "Chromo-H3"); REQUIRE((*(cross_linker.getModifsCstRef().cbegin() + 2)) ->getName() .toStdString() == "Chromo-H"); REQUIRE(cross_linker.hasModif("Chromo-O")); REQUIRE(cross_linker.modifIndex("Chromo-O") == 0); REQUIRE(cross_linker.hasModif("Chromo-H3")); REQUIRE(cross_linker.modifIndex("Chromo-H3") == 1); REQUIRE(cross_linker.hasModif("Chromo-H")); REQUIRE(cross_linker.modifIndex("Chromo-H") == 2); REQUIRE_FALSE(cross_linker.hasModif("Chromo-OOOO")); REQUIRE(cross_linker.modifIndex("Chromo-OOOO") == -1); REQUIRE(cross_linker.getModifAt(0) == (*cross_linker.getModifsCstRef().cbegin())); REQUIRE(cross_linker.getModifAt(2) == (*(std::prev(cross_linker.getModifsCstRef().cend())))); AND_THEN("The masses should be calculated fine") { REQUIRE(cross_linker.calculateMasses(nullptr)); REQUIRE_THAT(cross_linker.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(-38.036779434, 0.0000000001)); REQUIRE_THAT( cross_linker.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(-38.0464662446, 0.0000000001)); } } AND_WHEN("Modifications are removed from it piecemeal") { cross_linker.removeModifAt(1); REQUIRE(cross_linker.getModifsCstRef().size() == 2); REQUIRE(cross_linker.getModifAt(0) == (*cross_linker.getModifsCstRef().cbegin())); REQUIRE(cross_linker.getModifAt(1) == (*(std::prev(cross_linker.getModifsCstRef().cend())))); REQUIRE(cross_linker.modifIndex("Chromo-O") == 0); REQUIRE_FALSE(cross_linker.hasModif("Chromo-H3")); REQUIRE(cross_linker.modifIndex("Chromo-H") == 1); cross_linker.removeModifAt(1); REQUIRE(cross_linker.getModifsCstRef().size() == 1); REQUIRE(cross_linker.getModifAt(0) == (*cross_linker.getModifsCstRef().cbegin())); REQUIRE(cross_linker.modifIndex("Chromo-O") == 0); REQUIRE_FALSE(cross_linker.hasModif("Chromo-H")); cross_linker.removeModifAt(0); REQUIRE(cross_linker.getModifsCstRef().size() == 0); } } } } SCENARIO( "CrossLinker instances can be copy-constructed or assigned and compared", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN( "A CrossLinker is created with name and formula and then Modif are added") { CrossLinker cross_linker(pol_chem_def_csp, "CFP-chromophore", "-H2O"); // Get Modifs from the PolChemDef. ModifCstSPtr modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-O"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H"); REQUIRE(modif_csp != nullptr); cross_linker.appendModif(modif_csp); modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Chromo-H3"); cross_linker.insertModifAt(modif_csp, 1); THEN( "It is valid, does validate successfully and has all proper member " "data") { error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == "-H2O"); REQUIRE(cross_linker.getModifsCstRef().size() == 3); } AND_WHEN("A new CrossLinker is copy-constructed") { CrossLinker new_cross_linker(cross_linker); // qDebug() << "cross_linker:" << cross_linker.toString(); // qDebug() << "new_cross_linker:" << new_cross_linker.toString(); THEN("All the members should be identical in both instances") { REQUIRE(new_cross_linker.validate(error_list_cross_linker)); REQUIRE(new_cross_linker.isValid()); REQUIRE(new_cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(new_cross_linker.getFormula().toStdString() == "-H2O"); REQUIRE(new_cross_linker.getModifsCstRef().size() == 3); REQUIRE(cross_linker.getModifAt(0) == new_cross_linker.getModifAt(0)); REQUIRE(cross_linker.getModifAt(1) == new_cross_linker.getModifAt(1)); REQUIRE(cross_linker.getModifAt(2) == new_cross_linker.getModifAt(2)); AND_THEN("The comparison operators should provide correct results") { REQUIRE(new_cross_linker == cross_linker); REQUIRE_FALSE(new_cross_linker != cross_linker); } } } AND_WHEN("A new CrossLinker is assigned") { CrossLinker new_cross_linker; new_cross_linker = cross_linker; REQUIRE(cross_linker == (cross_linker = cross_linker)); // qDebug() << "cross_linker:" << cross_linker.toString(); // qDebug() << "new_cross_linker:" << new_cross_linker.toString(); THEN("All the members should be identical in both instances") { REQUIRE(new_cross_linker.validate(error_list_cross_linker)); REQUIRE(new_cross_linker.isValid()); REQUIRE(new_cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(new_cross_linker.getFormula().toStdString() == "-H2O"); REQUIRE(new_cross_linker.getModifsCstRef().size() == 3); REQUIRE(cross_linker.getModifAt(0) == new_cross_linker.getModifAt(0)); REQUIRE(cross_linker.getModifAt(1) == new_cross_linker.getModifAt(1)); REQUIRE(cross_linker.getModifAt(2) == new_cross_linker.getModifAt(2)); AND_THEN("The comparison operators should provide correct results") { REQUIRE(new_cross_linker == cross_linker); REQUIRE_FALSE(new_cross_linker != cross_linker); REQUIRE(cross_linker == cross_linker); REQUIRE_FALSE(cross_linker != cross_linker); } } } AND_WHEN("A new CrossLinker is copy-constructed but its name modified") { CrossLinker new_cross_linker(cross_linker); // qDebug() << "cross_linker:" << cross_linker.toString(); // qDebug() << "new_cross_linker:" << new_cross_linker.toString(); new_cross_linker.setName("other"); THEN("The comparison operators should provide correct results") { REQUIRE_FALSE(new_cross_linker == cross_linker); REQUIRE(new_cross_linker != cross_linker); } } AND_WHEN("A new CrossLinker is copy-constructed but its formula modified") { CrossLinker new_cross_linker(cross_linker); // qDebug() << "cross_linker:" << cross_linker.toString(); // qDebug() << "new_cross_linker:" << new_cross_linker.toString(); new_cross_linker.setFormula(""); THEN("The comparison operators should provide correct results") { REQUIRE_FALSE(new_cross_linker == cross_linker); REQUIRE(new_cross_linker != cross_linker); } } AND_WHEN("A new CrossLinker is copy-constructed but one Modif is removed") { CrossLinker new_cross_linker(cross_linker); // qDebug() << "cross_linker:" << cross_linker.toString(); // qDebug() << "new_cross_linker:" << new_cross_linker.toString(); new_cross_linker.removeModifAt(0); THEN("The comparison operators should provide correct results") { REQUIRE_FALSE(new_cross_linker == cross_linker); REQUIRE(new_cross_linker != cross_linker); } } } } SCENARIO( "CrossLinker instances have masses accounting for formula and Modif " "instances", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created by looking into PolChemDef with name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(cross_linker_csp != nullptr); CrossLinker cross_linker(*cross_linker_csp); THEN( "It is valid, does validate successfully and has all proper member " "data") { error_list_cross_linker.clear(); REQUIRE(cross_linker.validate(error_list_cross_linker)); REQUIRE(cross_linker.isValid()); REQUIRE(cross_linker.getName().toStdString() == "CFP-chromophore"); REQUIRE(cross_linker.getFormula().toStdString() == ""); REQUIRE(cross_linker.getModifsCstRef().size() == 3); } AND_WHEN("The masses are calculated explicitely with bad params") { CrossLinker another_cross_linker(cross_linker); another_cross_linker.setPolChemDefCstSPtr(nullptr); REQUIRE_FALSE(another_cross_linker.calculateMasses(nullptr)); another_cross_linker.setPolChemDefCstSPtr(pol_chem_def_csp); another_cross_linker.setFormula("%HWO"); REQUIRE_FALSE(another_cross_linker.calculateMasses(nullptr)); AND_WHEN("The masses are calculated explicitely with good params") { THEN("The masses are correct") { REQUIRE_THAT( cross_linker.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT( cross_linker.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); } AND_WHEN("The masses are accounted once (pointer version)") { double mono = 0; double avg = 0; REQUIRE(cross_linker == cross_linker.accountMasses(&mono, &avg, 1)); THEN("The masses are correct") { REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); } } AND_WHEN("The masses are accounted twice (pointer version)") { double mono = 0; double avg = 0; REQUIRE(cross_linker == cross_linker.accountMasses(&mono, &avg, 2)); THEN("The masses are correct") { REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(2 * -20.0262147493, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(2 * -20.0311745907, 0.0000000001)); } } AND_WHEN("The masses are accounted once (reference version)") { double mono = 0; double avg = 0; REQUIRE(cross_linker == cross_linker.accountMasses(mono, avg, 1)); THEN("The masses are correct") { REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(-20.0262147493, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(-20.0311745907, 0.0000000001)); } } AND_WHEN("The masses are accounted twice (reference version)") { double mono = 0; double avg = 0; REQUIRE(cross_linker == cross_linker.accountMasses(mono, avg, 2)); THEN("The masses are correct") { REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(2 * -20.0262147493, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(2 * -20.0311745907, 0.0000000001)); } } } } } } SCENARIO( "CrossLinker instances can be tested for existence in the PolChemDef by " "name", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created by looking into PolChemDef with name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(cross_linker_csp != nullptr); CrossLinker cross_linker(*cross_linker_csp); THEN("Its existence in the PolChemDef can be checked by name") { REQUIRE(cross_linker.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); } } } SCENARIO( "CrossLinker instances can write themselves out to an XML element", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created by looking into PolChemDef with name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(cross_linker_csp != nullptr); CrossLinker cross_linker(*cross_linker_csp); THEN("The XML element must be correct") { REQUIRE(cross_linker .formatXmlClkElement(/*offset*/ 0, /*indent*/ Utils::xmlIndentationToken) .toStdString() == "\n CFP-chromophore\n \n " "Chromo-O\n " "Chromo-H3\n " "Chromo-H\n\n"); } } } SCENARIO("CrossLinker instances can check for existence in the PolChemDef", "[CrossLinker]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_cross_linker.msp_polChemDef; WHEN("A CrossLinker is created by looking into PolChemDef with name") { CrossLinkerCstSPtr cross_linker_csp = pol_chem_def_csp->getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(cross_linker_csp != nullptr); CrossLinker cross_linker(*cross_linker_csp); THEN("That CrossLinker should be found in the PolChemDef") { REQUIRE(*cross_linker.getFromPolChemDefByName() == cross_linker); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Formula.cpp000664 001750 001750 00000132475 15100504560 021763 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_formula; ErrorList error_list_formula; SCENARIO("Construction of Formula with no action-formula string", "[Formula]") { test_utils_formula.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); WHEN("Constructed without action-formula") { Formula formula; THEN("The formula is invalid") { REQUIRE(formula.getActionFormula().toStdString() == ""); REQUIRE_FALSE(formula.isValid()); } WHEN("Explicitely validated with no arguments but the IsotopicData") { THEN("The formula cannot validate succesfully") { REQUIRE_FALSE(formula.validate(isotopic_data_csp, error_list_formula)); REQUIRE_FALSE(formula.isValid()); } } WHEN("Copy-constructed") { Formula new_formula(formula); THEN("The new Formula is also invalid") { REQUIRE(new_formula.getActionFormula().toStdString() == ""); REQUIRE_FALSE(new_formula.isValid()); } } AND_WHEN("Assignment operator copied") { Formula new_formula; new_formula = formula; THEN("The new Formula is also invalid") { REQUIRE(new_formula.getActionFormula().toStdString() == ""); REQUIRE_FALSE(new_formula.isValid()); } } WHEN("Setting the titled action-formula with the setter and a string") { formula.setActionFormula( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); // qDebug() << "The action-formula:" << formula.getActionFormula(); // qDebug() << "The action-formula with title:" << // formula.getActionFormula(true); THEN( "The formula is invalid (not validated yet), the title has been " "removed " "and is now in m_title") { REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } AND_WHEN("Appending to the action-formula an empty string") { formula.appendActionFormula(""); // qDebug() << "The action-formula:" << formula.getActionFormula(); // qDebug() << "The action-formula with title:" << // formula.getActionFormula(true); THEN("The formula is not changed whatsoever") { REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } AND_WHEN("The masses are accounted") { double mono; double avg; bool ok = false; formula.accountMasses(ok, isotopic_data_csp, mono, avg, /*times*/ 1); REQUIRE(ok); } } AND_WHEN( "Appending to the action-formula a non-empty string but with " "erroneous " "syntax") { bool result = formula.appendActionFormula("+h2O"); THEN("The formula is not changed whatsoever") { REQUIRE_FALSE(result); REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } } AND_WHEN( "Appending to the action-formula a non-empty string correct-syntax but " "with " "failing chemistry") { QString new_action_formula_string = test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl; new_action_formula_string += "+Hh2O"; bool result = formula.appendActionFormula("+Hh2O"); // qDebug() << "The action-formula:" << formula.getActionFormula(); // qDebug() << "The action-formula with title:" // << formula.getActionFormula(true); THEN("The formula is indeed changed") { REQUIRE(result); REQUIRE(formula.getActionFormula().toStdString() == new_action_formula_string.toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } AND_WHEN("That changed formula is validated") { THEN("The validation fails") { REQUIRE_FALSE( formula.validate(isotopic_data_csp, error_list_formula)); } AND_WHEN("The masses are accounted") { double mono; double avg; bool ok = false; formula.accountMasses( ok, isotopic_data_csp, mono, avg, /*times*/ 1); REQUIRE_FALSE(ok); } } } AND_WHEN( "Appending to the action-formula a valid non-empty string without " "title") { QString new_action_formula_string = test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl; new_action_formula_string += "+H2O"; bool result = formula.appendActionFormula("+H2O"); THEN("The formula is changed") { REQUIRE(result); REQUIRE(formula.getActionFormula().toStdString() == new_action_formula_string.toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } } AND_WHEN( "Appending to the action-formula a valid non-empty string with title") { QString new_action_formula_string = test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl; new_action_formula_string += "+H2O"; bool result = formula.appendActionFormula("\"Add water\"+H2O"); THEN("The formula is changed") { REQUIRE(result); REQUIRE(formula.getActionFormula().toStdString() == new_action_formula_string.toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } } AND_WHEN( "Resetting the titled action-formula with the setter to the empty " "string") { formula.setActionFormula(""); THEN( "The formula is invalid (not validated yet), the title has been " "removed " "and is now in m_title") { REQUIRE(formula.getActionFormula().toStdString() == ""); REQUIRE_FALSE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == ""); REQUIRE_FALSE(formula.isValid()); } } } WHEN("Setting the titled action-formula with the setter and a Formula") { Formula new_formula( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); formula.setActionFormula(new_formula); THEN( "The formula is invalid (not validated yet), the title has to be " "empty " "(was removed upon construction)") { REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == ""); REQUIRE_FALSE(formula.isValid()); } AND_WHEN( "Resetting the titled action-formula with the setter to the empty " "string via a Formula") { new_formula.setActionFormula(""); formula.setActionFormula(new_formula); THEN( "The formula is invalid (not validated yet), the title has been " "removed " "and is now in m_title") { REQUIRE(formula.getActionFormula().toStdString() == ""); REQUIRE_FALSE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == ""); REQUIRE_FALSE(formula.isValid()); } } } } } SCENARIO( "Construction and copying of Formula instances with titled action-formula " "string", "[Formula]") { WHEN("Constructed with a title action-formula") { Formula formula( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); THEN( "The formula is invalid (not validated yet), the title has been " "removed " "and is now in m_title") { REQUIRE( formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl.toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); } WHEN("Explicitely validated with no arguments but the IsotopicData") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); THEN("The formula validates succesfully") { REQUIRE(formula.validate(isotopic_data_csp, error_list_formula)); REQUIRE(formula.isValid()); } } AND_WHEN("Copy-constructed to a new Formula") { Formula new_formula(formula); THEN("All relevant members need to be identical") { REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); REQUIRE(new_formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(new_formula.checkSyntax()); REQUIRE(new_formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(new_formula.isValid()); } } AND_WHEN("Assignment-operator copied to a new Formula") { Formula new_formula; new_formula = formula; THEN("All relevant members need to be identical") { REQUIRE(formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula.checkSyntax()); REQUIRE(formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(formula.isValid()); REQUIRE(new_formula.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(new_formula.checkSyntax()); REQUIRE(new_formula.getTitle().toStdString() == test_utils_formula.m_formulaTitle.toStdString()); REQUIRE_FALSE(new_formula.isValid()); } } } } SCENARIO("Construction of Formula_s with a simple no-action string", "[Formula]") { WHEN("Constructed, using the constructor and the copy constructor") { Formula formula_c6h12O6(test_utils_formula.m_glucoseFormulaString); Formula formula_c6h12O6_copy(formula_c6h12O6); THEN("The the formula string is set to the member datum") { REQUIRE(formula_c6h12O6.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); REQUIRE(formula_c6h12O6_copy.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); } } AND_WHEN("Setting another formula string manually") { Formula formula_c6h12O6(test_utils_formula.m_glucoseFormulaString); formula_c6h12O6.setActionFormula( test_utils_formula.m_saccharoseFormulaString); THEN("The new formula string is set accordingly to the member datum") { REQUIRE(formula_c6h12O6.getActionFormula().toStdString() == test_utils_formula.m_saccharoseFormulaString.toStdString()); } } AND_WHEN("Appending another formula string") { Formula formula_c6h12O6(test_utils_formula.m_glucoseFormulaString); bool result = formula_c6h12O6.appendActionFormula( test_utils_formula.m_saccharoseFormulaString); QString after_appending_fomula(test_utils_formula.m_glucoseFormulaString); after_appending_fomula += test_utils_formula.m_saccharoseFormulaString; THEN("The member formula string is updated accordingly") { REQUIRE(result); REQUIRE(formula_c6h12O6.getActionFormula().toStdString() == after_appending_fomula.toStdString()); } } } SCENARIO("Construction of Formula_s with another Formula instance", "[Formula]") { Formula formula_c6h12O6(test_utils_formula.m_glucoseFormulaString); WHEN("Constructed with another Formula instance") { Formula formula_c6h12O6_copy(formula_c6h12O6); THEN("The Formula string is set to the member datum") { REQUIRE(formula_c6h12O6.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); REQUIRE(formula_c6h12O6_copy.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); } } } SCENARIO("Until validated, a Formula is invalid", "[Formula]") { WHEN( "Constructed, using the constructor, the copy constructor and the " "assignment operator") { Formula formula_c6h12O6(test_utils_formula.m_glucoseFormulaString); Formula formula_c6h12O6_copy(formula_c6h12O6); Formula formula_c6h12O6_assigned; formula_c6h12O6_assigned = formula_c6h12O6; THEN("The the formula string is set to the member datum") { REQUIRE(formula_c6h12O6.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); REQUIRE(formula_c6h12O6_copy.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); REQUIRE(formula_c6h12O6_assigned.getActionFormula().toStdString() == test_utils_formula.m_glucoseFormulaString.toStdString()); } THEN("Because not explicitely validated, the formulas are invalid") { REQUIRE_FALSE(formula_c6h12O6.isValid()); REQUIRE_FALSE(formula_c6h12O6_copy.isValid()); REQUIRE_FALSE(formula_c6h12O6_assigned.isValid()); } } } SCENARIO( "Syntax checking of an action Formula with or without the title is not a " "validation", "[Formula]") { Formula formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl); Formula titled_formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); WHEN("Checked without forcing an index even for single atoms") { formula_MA_dipeptidyl.setForceCountIndex(false); titled_formula_MA_dipeptidyl.setForceCountIndex(false); THEN("Checking succeeds") { REQUIRE(formula_MA_dipeptidyl.checkSyntax()); REQUIRE(titled_formula_MA_dipeptidyl.checkSyntax()); } THEN("The formula is not valid") { REQUIRE_FALSE(formula_MA_dipeptidyl.isValid()); REQUIRE_FALSE(titled_formula_MA_dipeptidyl.isValid()); } } AND_WHEN("Checked with forcing an index even for single atoms") { formula_MA_dipeptidyl.setForceCountIndex(true); titled_formula_MA_dipeptidyl.setForceCountIndex(true); THEN("Checking fails") { REQUIRE(formula_MA_dipeptidyl.checkSyntax() == false); REQUIRE(titled_formula_MA_dipeptidyl.checkSyntax() == false); } THEN("The formula is not valid") { REQUIRE_FALSE(formula_MA_dipeptidyl.isValid()); REQUIRE_FALSE(titled_formula_MA_dipeptidyl.isValid()); } } AND_WHEN("Checking an invalid Formula starting with a cipher") { Formula formula("3Cz3H12O6N14L2"); formula.setForceCountIndex(false); THEN("Checking fails") { REQUIRE(formula.checkSyntax() == false); } THEN("The formula is not valid") { REQUIRE_FALSE(formula_MA_dipeptidyl.isValid()); REQUIRE_FALSE(titled_formula_MA_dipeptidyl.isValid()); } } AND_WHEN( "Checking an invalid Formula ending not with an alphabetic character or " "a " "number") { Formula formula("Cz3H12O6N14L2§"); formula.setForceCountIndex(false); THEN("Checking fails") { REQUIRE(formula.checkSyntax() == false); } THEN("The formula is not valid") { REQUIRE_FALSE(formula_MA_dipeptidyl.isValid()); REQUIRE_FALSE(titled_formula_MA_dipeptidyl.isValid()); } } } SCENARIO( "Only an explicit validation of a Formula establishes its valid/unvalid " "status", "[Formula]") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN( "Two Formula instances constructed with valid actionformula strings " "either " "titled or not") { Formula formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl); Formula titled_formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); THEN("These have proper action-formulas stripped of title") { REQUIRE( formula_MA_dipeptidyl.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl.toStdString()); REQUIRE( titled_formula_MA_dipeptidyl.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl.toStdString()); } WHEN( "Explicitely validated with no arguments but the IsotopicData (const " "function)") { REQUIRE( formula_MA_dipeptidyl.validate(isotopic_data_csp, error_list_formula)); REQUIRE(formula_MA_dipeptidyl.isValid()); REQUIRE(titled_formula_MA_dipeptidyl.validate(isotopic_data_csp, error_list_formula)); REQUIRE(titled_formula_MA_dipeptidyl.isValid()); THEN( "The + and - sub formulas are not set because we did not ask for " "storing") { // "+C5H11N1O2S1-H20+C3H7N1O2-H2O" REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == ""); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == ""); REQUIRE(titled_formula_MA_dipeptidyl.getPlusFormula().toStdString() == ""); REQUIRE(titled_formula_MA_dipeptidyl.getMinusFormula().toStdString() == ""); } THEN( "The symbol counts cannot be used to craft a new integrated " "elemental composition string formula because we did not store them") { REQUIRE(formula_MA_dipeptidyl.getSymbolCountMapCstRef().size() == 0); std::vector> symbol_count_pairs; QString elemental_composition = formula_MA_dipeptidyl.elementalComposition(&symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == ""); elemental_composition = titled_formula_MA_dipeptidyl.elementalComposition( &symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == ""); const std::map &formula_symbol_count_map = formula_MA_dipeptidyl.getSymbolCountMapCstRef(); REQUIRE(formula_symbol_count_map.size() == 0); } } WHEN( "Explicitely validated with reset and store set to true, the Formula " "creates + and - subformulas") { REQUIRE(formula_MA_dipeptidyl.validate( isotopic_data_csp, /*store*/ true, /*reset*/ true, error_list_formula)); REQUIRE(formula_MA_dipeptidyl.isValid()); REQUIRE(titled_formula_MA_dipeptidyl.validate( isotopic_data_csp, /*store*/ true, /*reset*/ true, error_list_formula)); REQUIRE(titled_formula_MA_dipeptidyl.isValid()); THEN("The + and - sub formulas are checked") { // "+C5H11N1O2S1-H20+C3H7N1O2-H2O" REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); REQUIRE(titled_formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(titled_formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); AND_THEN( "The symbol counts can be used to craft a new integrated " "elemental composition string formula") { // Expecting result of "C5H11N1O2S1C3H7N1O2" - "H2O1H2O1" // which is C8H18N2O4S1 - H4O2 // which is C8H14N2O2S1 std::vector> symbol_count_pairs; QString elemental_composition = formula_MA_dipeptidyl.elementalComposition(&symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == "C8H14N2O2S1"); elemental_composition = titled_formula_MA_dipeptidyl.elementalComposition( &symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == "C8H14N2O2S1"); const std::map &formula_symbol_count_map = formula_MA_dipeptidyl.getSymbolCountMapCstRef(); auto map_iterator = formula_symbol_count_map.find("C"); REQUIRE(map_iterator->second == 8); map_iterator = formula_symbol_count_map.find("H"); REQUIRE(map_iterator->second == 14); map_iterator = formula_symbol_count_map.find("N"); REQUIRE(map_iterator->second == 2); map_iterator = formula_symbol_count_map.find("O"); REQUIRE(map_iterator->second == 2); map_iterator = formula_symbol_count_map.find("S"); REQUIRE(map_iterator->second == 1); } } AND_WHEN( "Explicitely validated with reset set to false and store set to " "true, " "the action-formula increments but not the + and - subformulas") { REQUIRE(formula_MA_dipeptidyl.validate(isotopic_data_csp, /*store*/ true, /*reset*/ false, error_list_formula)); REQUIRE(formula_MA_dipeptidyl.isValid()); REQUIRE(titled_formula_MA_dipeptidyl.validate(isotopic_data_csp, /*store*/ true, /*reset*/ false, error_list_formula)); REQUIRE(titled_formula_MA_dipeptidyl.isValid()); THEN("The + and - sub formulas are checked") { // "+C5H11N1O2S1-H20+C3H7N1O2-H2O" REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); REQUIRE(titled_formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE( titled_formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); } AND_THEN( "The symbol counts can be used to craft a new integrated " "elemental composition string formula") { // Expecting result of "C5H11N1O2S1C3H7N1O2" - "H2O1H2O1" // which is C8H18N2O4S1 - H4O2 // which is C8H14N2O2S1 std::vector> symbol_count_pairs; QString elemental_composition = formula_MA_dipeptidyl.elementalComposition(&symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == "C16H28N4O4S2"); elemental_composition = titled_formula_MA_dipeptidyl.elementalComposition( &symbol_count_pairs); REQUIRE(elemental_composition.toStdString() == "C16H28N4O4S2"); const std::map &formula_symbol_count_map = formula_MA_dipeptidyl.getSymbolCountMapCstRef(); auto map_iterator = formula_symbol_count_map.find("C"); REQUIRE(map_iterator->second == 16); map_iterator = formula_symbol_count_map.find("H"); REQUIRE(map_iterator->second == 28); map_iterator = formula_symbol_count_map.find("N"); REQUIRE(map_iterator->second == 4); map_iterator = formula_symbol_count_map.find("O"); REQUIRE(map_iterator->second == 4); map_iterator = formula_symbol_count_map.find("S"); REQUIRE(map_iterator->second == 2); } } } } } SCENARIO("Formula validation can fail in a number of ways", "[Formula]") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("Failing formula strings and failing formulas constructed with these") { QString invalid_formula_string = "+h20"; QString invalid_formula_string_with_title = "\"invalid\"+h20"; Formula invalid_formula(invalid_formula_string); Formula invalid_formula_with_title(invalid_formula_string_with_title); REQUIRE_FALSE(invalid_formula.isValid()); REQUIRE_FALSE(invalid_formula_with_title.isValid()); WHEN("Explicitely validated against reference isotopic data") { REQUIRE_FALSE( invalid_formula.validate(isotopic_data_csp, error_list_formula)); REQUIRE_FALSE(invalid_formula_with_title.validate(isotopic_data_csp, error_list_formula)); THEN("They persit to be invalid") { REQUIRE_FALSE(invalid_formula.isValid()); REQUIRE_FALSE(invalid_formula_with_title.isValid()); } AND_WHEN( "The failing formulas are modified with valid string formulas using " "setFormula()") { invalid_formula.setActionFormula("+H2O"); invalid_formula_with_title.setActionFormula("\"Fixed\"+H2O"); THEN( "The formulas are still invalid because it was not explicitely " "validated") { REQUIRE_FALSE(invalid_formula.isValid()); REQUIRE_FALSE(invalid_formula_with_title.isValid()); } AND_WHEN("The formula is explicitely validated") { REQUIRE( invalid_formula.validate(isotopic_data_csp, error_list_formula)); REQUIRE(invalid_formula_with_title.validate(isotopic_data_csp, error_list_formula)); THEN("It becomes valid because the new formula was correct") { REQUIRE(invalid_formula.isValid()); REQUIRE(invalid_formula_with_title.isValid()); } AND_WHEN("The formula is explicitely cleared") { invalid_formula.clear(); THEN("All the member data are reset") { REQUIRE(invalid_formula.getTitle().toStdString() == ""); REQUIRE(invalid_formula.getActionFormula().toStdString() == ""); REQUIRE(invalid_formula.getPlusFormula().toStdString() == ""); REQUIRE(invalid_formula.getMinusFormula().toStdString() == ""); REQUIRE(invalid_formula.isForceCountIndex() == false); REQUIRE(invalid_formula.getSymbolCountMapCstRef().size() == 0); } } } } } } } SCENARIO( "It is possible to account an action-formula string into an existing " "Formula instance", "[Formula]") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("A Formula created from a valid formula string") { Formula formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl); WHEN("That formula is validated succesfully with store set to true") { REQUIRE(formula_MA_dipeptidyl.validate( isotopic_data_csp, /*store*/ true, /*reset*/ true, error_list_formula)); REQUIRE(formula_MA_dipeptidyl.isValid()); THEN("The sub-formulas need to be correct") { REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); } AND_WHEN("An invalid string action-formula is accounted for") { QString invalid_action_formula_string("+h2O"); bool ok = false; int times = 2; formula_MA_dipeptidyl.accountFormula( invalid_action_formula_string, isotopic_data_csp, times, ok); THEN("The new action formula has to be unchanged") { REQUIRE(formula_MA_dipeptidyl.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); } AND_WHEN("A valid string action-formula is accounted for") { QString invalid_action_formula_string("+H2O"); bool ok = false; int times = 2; formula_MA_dipeptidyl.accountFormula( invalid_action_formula_string, isotopic_data_csp, times, ok); THEN("The new action formula has to be unchanged") { REQUIRE(formula_MA_dipeptidyl.getActionFormula().toStdString() == "C8H18N2O4S1"); } } } } } } SCENARIO("Various ways to account a Formula for its masses (mono, avg)", "[Formula]") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("A Formula created from a valid formula string") { Formula formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl); WHEN("That formula is validated succesfully") { REQUIRE(formula_MA_dipeptidyl.validate(isotopic_data_csp, /*store*/ true, /*reset*/ false, error_list_formula)); THEN("It becomes valid") { REQUIRE(formula_MA_dipeptidyl.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula_MA_dipeptidyl.isValid()); } AND_WHEN( "That formula is validated with adequate options, its other member " "data are updated") { formula_MA_dipeptidyl.validate(isotopic_data_csp, error_list_formula); THEN("The + and - sub formulas are checked") { // "+C5H11N1O2S1-H20+C3H7N1O2-H2O" REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); AND_THEN("The formula becomes valid") { REQUIRE(formula_MA_dipeptidyl.isValid()); } } AND_WHEN("Masses are accounted for the action-formula") { THEN("Mono and avg masses are updated in ony possible way") { double mono = 0; double avg = 0; bool ok; formula_MA_dipeptidyl.accountMasses( ok, isotopic_data_csp, mono, avg, 1); REQUIRE(ok == true); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(202.07759887468, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(202.274890194285717, 0.0000000001)); } AND_THEN("In another possible way") { double mono = 0; double avg = 0; bool ok; formula_MA_dipeptidyl.accountMasses( ok, isotopic_data_csp, mono, avg, 10); REQUIRE(ok == true); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(202.07759887468 * 10, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(202.274890194285717 * 10, 0.0000000001)); } AND_WHEN("The formula is compared to itself") { THEN("True is returned for ==()") { REQUIRE(formula_MA_dipeptidyl == formula_MA_dipeptidyl); } THEN("False is returned for !=()") { REQUIRE_FALSE(formula_MA_dipeptidyl != formula_MA_dipeptidyl); } } AND_WHEN("Another formula is copy-constructed") { const Formula another_formula(formula_MA_dipeptidyl); THEN("The two Formula instances should compare positively") { REQUIRE(another_formula == formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula != formula_MA_dipeptidyl); REQUIRE(another_formula.getSymbolCountMapCstRef().size() == formula_MA_dipeptidyl.getSymbolCountMapCstRef().size()); REQUIRE(another_formula.getSymbolCountMapCstRef() == formula_MA_dipeptidyl.getSymbolCountMapCstRef()); // This is the formula: // "+C5H11N1O2S1-H2O+C3H7N1O2-H2O" // equivalent to: // "+C5H11N1O2S1+C3H7N1O2-H4O2" // equivalent to: // "+C8H18N2O4S1-H4O2" // that is: // "C8H14N2O2S1" // So we can check the atom symbol count map. qDebug() << "Compare atom symbol count for C"; auto iter = formula_MA_dipeptidyl.getSymbolCountMapCstRef().find("C"); auto another_iter = another_formula.getSymbolCountMapCstRef().find("C"); REQUIRE(iter != formula_MA_dipeptidyl.getSymbolCountMapCstRef().end()); REQUIRE(another_iter != another_formula.getSymbolCountMapCstRef().end()); REQUIRE((*iter).second == (*another_iter).second); qDebug() << "Compare atom symbol count for H"; iter = formula_MA_dipeptidyl.getSymbolCountMapCstRef().find("H"); another_iter = another_formula.getSymbolCountMapCstRef().find("H"); REQUIRE(iter != formula_MA_dipeptidyl.getSymbolCountMapCstRef().end()); REQUIRE(another_iter != another_formula.getSymbolCountMapCstRef().end()); REQUIRE((*iter).second == (*another_iter).second); qDebug() << "Compare atom symbol count for N"; iter = formula_MA_dipeptidyl.getSymbolCountMapCstRef().find("N"); another_iter = another_formula.getSymbolCountMapCstRef().find("N"); REQUIRE(iter != formula_MA_dipeptidyl.getSymbolCountMapCstRef().end()); REQUIRE(another_iter != another_formula.getSymbolCountMapCstRef().end()); REQUIRE((*iter).second == (*another_iter).second); qDebug() << "Compare atom symbol count for O"; iter = formula_MA_dipeptidyl.getSymbolCountMapCstRef().find("O"); another_iter = another_formula.getSymbolCountMapCstRef().find("O"); REQUIRE(iter != formula_MA_dipeptidyl.getSymbolCountMapCstRef().end()); REQUIRE(another_iter != another_formula.getSymbolCountMapCstRef().end()); REQUIRE((*iter).second == (*another_iter).second); qDebug() << "Compare atom symbol count for S"; iter = formula_MA_dipeptidyl.getSymbolCountMapCstRef().find("S"); another_iter = another_formula.getSymbolCountMapCstRef().find("S"); REQUIRE(iter != formula_MA_dipeptidyl.getSymbolCountMapCstRef().end()); REQUIRE(another_iter != another_formula.getSymbolCountMapCstRef().end()); REQUIRE((*iter).second == (*another_iter).second); } } AND_WHEN("Another formula is assignment-operator copied") { Formula another_formula; another_formula = formula_MA_dipeptidyl; THEN("The two Formula instances should compare positively") { REQUIRE(another_formula == formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula != formula_MA_dipeptidyl); } } AND_WHEN( "Another formula is copy-constructed, but later modified on title") { Formula another_formula(formula_MA_dipeptidyl); another_formula.setTitle("Different"); THEN("The two Formula instances should not compare positively") { REQUIRE(another_formula != formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula == formula_MA_dipeptidyl); } } AND_WHEN( "Another formula is assignment-operator copied, but later " "modified " "on title") { Formula another_formula; another_formula = formula_MA_dipeptidyl; another_formula.setTitle("Different"); THEN("The two Formula instances should not compare positively") { REQUIRE(another_formula != formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula == formula_MA_dipeptidyl); } } AND_WHEN( "Another formula is copy-constructed, but later modified on " "action-formula") { Formula another_formula(formula_MA_dipeptidyl); another_formula.setActionFormula("-H2O"); THEN("The two Formula instances should not compare positively") { REQUIRE(another_formula != formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula == formula_MA_dipeptidyl); } } AND_WHEN( "Another formula is assignment-operator copied, but later " "modified " "on action-formula") { Formula another_formula; another_formula = formula_MA_dipeptidyl; another_formula.setActionFormula("-H2O"); THEN("The two Formula instances should not compare positively") { REQUIRE(another_formula != formula_MA_dipeptidyl); REQUIRE_FALSE(another_formula == formula_MA_dipeptidyl); } } } } } } } SCENARIO("Formula instances can be scrutinized in various ways", "[Formula]") { IsotopicDataLibraryHandler isotopic_data_library_handler; qsizetype non_isotope_skipped_items = 0; qsizetype loaded_isotope_count = isotopic_data_library_handler.loadData(non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = nullptr; REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); isotopic_data_csp = isotopic_data_library_handler.getIsotopicData(); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("A Formula created from a valid formula string") { Formula formula_MA_dipeptidyl( test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl); WHEN("That formula is validated succesfully") { REQUIRE(formula_MA_dipeptidyl.validate(isotopic_data_csp, /*store*/ true, /*reset*/ false, error_list_formula)); THEN("It becomes valid") { REQUIRE(formula_MA_dipeptidyl.getActionFormula().toStdString() == test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl .toStdString()); REQUIRE(formula_MA_dipeptidyl.isValid()); } THEN("The sub-formulas can be checked") { REQUIRE(formula_MA_dipeptidyl.getPlusFormula().toStdString() == "C5H11N1O2S1C3H7N1O2"); REQUIRE(formula_MA_dipeptidyl.getMinusFormula().toStdString() == "H2O1H2O1"); } THEN("The elemental composition can be checked") { REQUIRE(formula_MA_dipeptidyl.elementalComposition() == "C8H14N2O2S1"); } THEN("The actions can be checked") { REQUIRE(formula_MA_dipeptidyl.actions() == '-'); } } } } SCENARIO("A Formula instance can initialize itself using an XML element") { // int formula_element_index = 0; // int formula_text_index = 1; QStringList dom_strings{"formula", "\"Protonation\"+H"}; QDomDocument document = test_utils_formula.craftFormulaDomDocument(dom_strings); QDomElement formula_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // qDebug() << "The document:" << document.toString(); WHEN( "An empty Formula instance is initialized by rendering in it the XML " "element") { Formula formula; REQUIRE(formula.renderXmlFormulaElement(formula_element) == true); THEN("The formula shoul have its members correctly set") { REQUIRE(formula.getActionFormula().toStdString() == "+H"); REQUIRE(formula.getTitle().toStdString() == "Protonation"); } } } SCENARIO("A Formula instance can document itself in an XML element") { Formula titled_formula( test_utils_formula.m_actionFormulaStringMetAlaDipeptidylTitled); WHEN("Documenting itself in an XML element") { QString xml_element = titled_formula.formatXmlFormulaElement(0, Utils::xmlIndentationToken); THEN("The string should match the expected") { REQUIRE(xml_element.toStdString() == QString("\"%1\"%2\n") .arg(test_utils_formula.m_formulaTitle) .arg(test_utils_formula.m_actionFormulaStringMetAlaDipeptidyl) .toStdString()); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_FragmentationConfig.cpp000664 001750 001750 00000017353 15100504560 024277 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_fragmentation_config_1_letter("protein-1-letter", 1); TestUtils test_utils_fragmentation_config_3_letters("protein-3-letters", 1); ErrorList error_list_fragmentation_config; SCENARIO("FragmentationConfig objects can be constructed in various ways", "[FragmentationConfig]") { test_utils_fragmentation_config_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_fragmentation_config_1_letter.msp_polChemDef; WHEN( "Constructing of a FragmentationConfig instances with or without explicit " "FragmentationPathway object use") { FragmentationConfig fragmentation_config_1(pol_chem_def_csp, "a", "-C1O1", Enums::FragEnd::LE, "frag_a_comment", /*sequence_embedded*/ true); fragmentation_config_1.setStartIndex(0); fragmentation_config_1.setStopIndex(120); fragmentation_config_1.setStartIonizeLevel(1); fragmentation_config_1.setStopIonizeLevel(3); FragmentationPathway fragmentation_pathway( pol_chem_def_csp, "a", "-C1O1", Enums::FragEnd::LE, "frag_a_comment"); // Trick to test the setIonizeLevels() function. FragmentationConfig fragmentation_config_2( fragmentation_pathway, 5, 8, /*sequence_embedded*/ true); fragmentation_config_2.setStartIndex(0); fragmentation_config_2.setStopIndex(120); fragmentation_config_2.setIonizeLevels(1, 3); fragmentation_config_1.addFormula(Formula("-H2O")); fragmentation_config_1.addFormula(Formula("-NH3")); fragmentation_config_2.addFormula(Formula("-H2O")); fragmentation_config_2.addFormula(Formula("-NH3")); THEN("All the member data are set correctly for both instances") { REQUIRE(fragmentation_config_1.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(fragmentation_config_1.getName().toStdString() == "a"); REQUIRE(fragmentation_config_1.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_config_1.getFormulaRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_config_1.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_config_1.getComment().toStdString() == "frag_a_comment"); REQUIRE(fragmentation_config_1.getRulesCstRef().size() == 0); REQUIRE(fragmentation_config_1.getRulesRef().size() == 0); REQUIRE(fragmentation_config_1.getStartIonizeLevel() == 1); REQUIRE(fragmentation_config_1.getStopIonizeLevel() == 3); REQUIRE(fragmentation_config_1.isSequenceEmbedded() == true); REQUIRE(fragmentation_config_1.getFormulasRef().size() == 2); REQUIRE(fragmentation_config_1.getFormulasCstRef().size() == 2); REQUIRE(fragmentation_config_1.getFormulasCstRef() .front() .getActionFormula() .toStdString() == "-H2O"); REQUIRE(fragmentation_config_1.getFormulasCstRef() .back() .getActionFormula() .toStdString() == "-NH3"); REQUIRE(fragmentation_config_2.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(fragmentation_config_2.getName().toStdString() == "a"); REQUIRE(fragmentation_config_2.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_config_2.getFormulaRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_config_2.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_config_2.getComment().toStdString() == "frag_a_comment"); REQUIRE(fragmentation_config_2.getRulesCstRef().size() == 0); REQUIRE(fragmentation_config_2.getRulesRef().size() == 0); REQUIRE(fragmentation_config_2.getStartIonizeLevel() == 1); REQUIRE(fragmentation_config_2.getStopIonizeLevel() == 3); REQUIRE(fragmentation_config_2.isSequenceEmbedded() == true); REQUIRE(fragmentation_config_2.getFormulasRef().size() == 2); REQUIRE(fragmentation_config_2.getFormulasCstRef().size() == 2); REQUIRE(fragmentation_config_2.getFormulasCstRef() .front() .getActionFormula() .toStdString() == "-H2O"); REQUIRE(fragmentation_config_2.getFormulasCstRef() .back() .getActionFormula() .toStdString() == "-NH3"); AND_WHEN("The copy constructor or the assignment operator are used") { FragmentationConfig another_fragmentation_config( fragmentation_config_1); FragmentationConfig other_fragmentation_config( pol_chem_def_csp, "NOT_SET", "NOT_SET"); other_fragmentation_config = another_fragmentation_config; THEN("All the FragmentationConfig need to be identical") { REQUIRE(other_fragmentation_config.getPolchemDefCstSPtr() == pol_chem_def_csp); REQUIRE(other_fragmentation_config.getName().toStdString() == "a"); REQUIRE(other_fragmentation_config.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(other_fragmentation_config.getFormulaRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(other_fragmentation_config.getFragEnd() == Enums::FragEnd::LE); REQUIRE(other_fragmentation_config.getComment().toStdString() == "frag_a_comment"); REQUIRE(other_fragmentation_config.getRulesCstRef().size() == 0); REQUIRE(other_fragmentation_config.getRulesRef().size() == 0); REQUIRE(other_fragmentation_config.getStartIonizeLevel() == 1); REQUIRE(other_fragmentation_config.getStopIonizeLevel() == 3); REQUIRE(other_fragmentation_config.isSequenceEmbedded() == true); REQUIRE(other_fragmentation_config.getFormulasRef().size() == 2); REQUIRE(other_fragmentation_config.getFormulasCstRef().size() == 2); REQUIRE(other_fragmentation_config.getFormulasCstRef() .front() .getActionFormula() .toStdString() == "-H2O"); REQUIRE(other_fragmentation_config.getFormulasCstRef() .back() .getActionFormula() .toStdString() == "-NH3"); AND_WHEN("The FragmentationConfig outputs itself as a string") { QString text = other_fragmentation_config.toString(); text = text.remove(QRegularExpression("\\s")); QString expected_text = QString("FragmentationConfig:\nFragmentation pathway:\nName: a - Formula: -C1O1 - Frag end: LE - Monomer contribution: 0\nisValid: true\n\nIndices [0-120] -- ionization levels [1-3].\nFormulas:\n-H2O -\n-NH3 -\n"); expected_text = expected_text.remove(QRegularExpression("\\s")); THEN("The string matches the expected string.") { REQUIRE(text.toStdString() == expected_text.toStdString()); } } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_FragmentationPathway.cpp000664 001750 001750 00000052407 15100504560 024506 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_fragmentation_pathway_1_letter("protein-1-letter", 1); TestUtils test_utils_fragmentation_pathway_3_letters("protein-3-letters", 1); ErrorList error_list_fragmentation_pathway; SCENARIO( "FragmentationPathway objects can be constructed empty and then initialized " "piecemeal " "until they are valid", "[FragmentationPathway]") { test_utils_fragmentation_pathway_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_fragmentation_pathway_1_letter.msp_polChemDef; GIVEN("A FragmentationPathway constructed with no parameter at all") { FragmentationPathway fragmentation_pathway; REQUIRE_FALSE(fragmentation_pathway.isValid()); REQUIRE_FALSE( fragmentation_pathway.validate(error_list_fragmentation_pathway)); // // a // LE // -C1O1 // opt_comment // // one_rule // +H2O // M // Y // T // opt_comment // // other fgr allowed, none possible also // WHEN("The PolChemDef is set with the setter") { fragmentation_pathway.setPolchemDefCstSPtr(pol_chem_def_csp); THEN( "The FragmentationPathway is still invalid and does not validate " "successfully") { REQUIRE_FALSE(fragmentation_pathway.isValid()); REQUIRE_FALSE( fragmentation_pathway.validate(error_list_fragmentation_pathway)); } AND_WHEN("The name is set with the setter") { fragmentation_pathway.setName("a"); THEN( "The FragmentationPathway is still invalid and does not validate " "successfully") { REQUIRE_FALSE(fragmentation_pathway.isValid()); REQUIRE_FALSE( fragmentation_pathway.validate(error_list_fragmentation_pathway)); AND_THEN("A FragmentationPathway can be gotten from the PolChemDef") { FragmentationPathwayCstSPtr fragmentation_pathway_csp = fragmentation_pathway.getFromPolChemDefByName(); REQUIRE(fragmentation_pathway_csp != nullptr); REQUIRE(fragmentation_pathway.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); } } AND_WHEN("The end is set with the setter") { fragmentation_pathway.setFragEnd(Enums::FragEnd::LE); THEN( "The FragmentationPathway is still invalid and does not validate " "successfully") { REQUIRE_FALSE(fragmentation_pathway.isValid()); REQUIRE_FALSE( fragmentation_pathway.validate(error_list_fragmentation_pathway)); } AND_WHEN("The formula is set with one of the setters") { fragmentation_pathway.setFormula("-C1O1"); THEN( "The FragmentationPathway is valid and does validate " "successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate( error_list_fragmentation_pathway)); } AND_WHEN("The formula is set with other of the setters") { fragmentation_pathway.setFormula(Formula("-C1O1")); THEN( "The FragmentationPathway is valid and does validate " "successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate( error_list_fragmentation_pathway)); } AND_WHEN("The comment is set with the setter") { fragmentation_pathway.setComment("opt_comment"); THEN( "The FragmentationPathway is still valid and does validate " "successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate( error_list_fragmentation_pathway)); } AND_WHEN("A correct FragmentationRule is added") { // // one_rule // +H2O // M // Y // T // opt_comment // FragmentationRuleSPtr frag_rule_sp = std::make_shared(pol_chem_def_csp, "one_rule", "F", "D", "E", "opt_comment", "+H2O"); fragmentation_pathway.addRule(frag_rule_sp); THEN( "The FragmentationPathway is still valid and does validate " "successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate( error_list_fragmentation_pathway)); REQUIRE(fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE(fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "one_rule"); } } } } } } } } } } SCENARIO( "FragmentationPathway objects can get as many FragmentationRule instances " "as " "necessary", "[FragmentationPathway]") { test_utils_fragmentation_pathway_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_fragmentation_pathway_1_letter.msp_polChemDef; GIVEN( "A FragmentationPathway constructed with full parameters (with " "FragmentationRules)") { FragmentationPathway fragmentation_pathway( pol_chem_def_csp, "a", "-C1O1", Enums::FragEnd::LE, "opt_comment"); FragmentationRuleSPtr frag_rule_1_sp = std::make_shared( pol_chem_def_csp, "one_rule", "F", "D", "E", "opt1_comment", "+H2O"); FragmentationRuleSPtr frag_rule_2_sp = std::make_shared( pol_chem_def_csp, "two_rule", "G", "E", "F", "opt2_comment", "+H4O2"); FragmentationRuleSPtr frag_rule_3_sp = std::make_shared( pol_chem_def_csp, "three_rule", "H", "F", "G", "opt3_comment", "+H6O9"); fragmentation_pathway.addRule(frag_rule_1_sp); fragmentation_pathway.addRule(frag_rule_2_sp); fragmentation_pathway.insertRuleAt(frag_rule_3_sp, 1); THEN("The FragmentationPathway is valid and does validate successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 3); REQUIRE( fragmentation_pathway.getRulesCstRef().at(0)->getName().toStdString() == "one_rule"); REQUIRE( fragmentation_pathway.getRulesCstRef().at(1)->getName().toStdString() == "three_rule"); REQUIRE( fragmentation_pathway.getRulesCstRef().at(2)->getName().toStdString() == "two_rule"); } WHEN("Copy-constructing a new FragmentationPathway") { FragmentationPathway new_fragmentation_pathway(fragmentation_pathway); THEN( "The new FragmentationPathway is valid, does validate successfully " "and operator==() returns proper result") { REQUIRE(new_fragmentation_pathway.isValid()); REQUIRE( new_fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(new_fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(new_fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(new_fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(new_fragmentation_pathway.getRulesCstRef().size() == 3); REQUIRE(new_fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "one_rule"); REQUIRE(new_fragmentation_pathway.getRulesCstRef() .at(1) ->getName() .toStdString() == "three_rule"); REQUIRE(new_fragmentation_pathway.getRulesCstRef() .at(2) ->getName() .toStdString() == "two_rule"); REQUIRE(new_fragmentation_pathway == fragmentation_pathway); } } WHEN("One FragmentationRule is removed") { fragmentation_pathway.removeRuleAt(0); THEN("The container changes") { REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 2); REQUIRE(fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "three_rule"); REQUIRE(fragmentation_pathway.getRulesCstRef() .at(1) ->getName() .toStdString() == "two_rule"); } WHEN("One FragmentationRule is removed") { fragmentation_pathway.removeRuleAt(1); THEN("The container changes") { REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE(fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "three_rule"); } } } } } SCENARIO( "FragmentationPathway objects can be copy- or assignment-constructed and " "compared", "[FragmentationPathway]") { test_utils_fragmentation_pathway_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_fragmentation_pathway_1_letter.msp_polChemDef; GIVEN( "A FragmentationPathway constructed with full parameters (with " "FragmentationRules)") { FragmentationPathway fragmentation_pathway( pol_chem_def_csp, "a", "-C1O1", Enums::FragEnd::LE, "opt_comment"); FragmentationRuleSPtr frag_rule_sp = std::make_shared( pol_chem_def_csp, "one_rule", "F", "D", "E", "opt_comment", "+H2O"); fragmentation_pathway.addRule(frag_rule_sp); THEN("The FragmentationPathway is valid and does validate successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE(fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE( fragmentation_pathway.getRulesCstRef().at(0)->getName().toStdString() == "one_rule"); } WHEN("Another FragmentationPathway is copy-constructed") { FragmentationPathway new_fragmentation_pathway(fragmentation_pathway); THEN("It is identical to the original one") { REQUIRE(new_fragmentation_pathway.isValid()); REQUIRE( new_fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(new_fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(new_fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(new_fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(new_fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE(new_fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "one_rule"); } THEN("The comparison operators work") { REQUIRE(new_fragmentation_pathway == fragmentation_pathway); REQUIRE_FALSE(new_fragmentation_pathway != fragmentation_pathway); REQUIRE(new_fragmentation_pathway == new_fragmentation_pathway); REQUIRE_FALSE(new_fragmentation_pathway != new_fragmentation_pathway); } } WHEN("Another FragmentationPathway is assignment-initialized") { FragmentationPathway new_fragmentation_pathway; new_fragmentation_pathway = fragmentation_pathway; THEN("It is identical to the original one") { REQUIRE(new_fragmentation_pathway.isValid()); REQUIRE( new_fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(new_fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(new_fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(new_fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(new_fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE(new_fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "one_rule"); } THEN("The comparison operators work") { REQUIRE(new_fragmentation_pathway == fragmentation_pathway); REQUIRE_FALSE(new_fragmentation_pathway != fragmentation_pathway); REQUIRE(new_fragmentation_pathway == new_fragmentation_pathway); REQUIRE_FALSE(new_fragmentation_pathway != new_fragmentation_pathway); } } } } SCENARIO( "FragmentationPathway objects from version 1 XML element can be " "initialized " "from an XML element and can export themselves as such also", "[FragmentationPathway]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_fragmentation_pathway_1_letter.msp_polChemDef; // int fgs_element_index = 0; // // // int name_element_index = 1; // int name_text_index = 2; // int frag_end_element_index = 3; // int frag_end_text_index = 4; // int formula_element_index = 5; // int formula_text_index = 6; // int side_chain_element_index = 7; // int side_chain_text_index = 8; // int comment_element_index = 9; // int comment_text_index = 10; // // // int fgr_element_index = 11; // // // int fgr_name_element_index = 12; // int fgr_name_text_index = 13; // int fgr_formula_element_index = 14; // int fgr_formula_text_index = 15; // int prev_code_element_index = 16; // int prev_code_text_index = 17; // int curr_code_element_index = 18; // int curr_code_text_index = 19; // int next_code_element_index = 20; // int next_code_text_index = 21; // int fgr_comment_element_index = 22; // int fgr_comment_text_index = 23; // // a // LE // -C1O1 // 0 // opt_comment // // one_rule // +H2O // M // Y // T // opt_comment // // other fgr allowed, none possible also // QStringList dom_strings{"fgs", // 0 "name", // 1 "a", // 2 "end", // 3 "LE", // 4 "formula", // 5 "-C1O1", // 6 "sidechaincontrib", // 7 "0", // 8 "comment", // 9 "opt_comment", // 10 "fgr", // 11 "name", // 12 "one_rule", // 13 "formula", // 14 "+H2O", // 15 "prev-mnm-code", // 16 "M", // 17 "curr-mnm-code", // 18 "Y", // 19 "next-mnm-code", // 20 "T", // 21 "comment", // 22 "opt_comment"}; // 23 QDomDocument document = test_utils_fragmentation_pathway_1_letter.craftFgsDomDocument(dom_strings); QDomElement fragmentation_pathway_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare // them. There must be a bug somewhere because with the original output, at // the screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); QString expected_fgs_string = "aLE-C1O1" "0opt_comment" "one_rule+H2O" "MY" "Topt_comment" ""; expected_fgs_string = Utils::unspacify(expected_fgs_string); QString expected_fgp_string = "aLE-C1O1" "0opt_comment" "one_rule+H2O" "MY" "Topt_comment" ""; expected_fgp_string = Utils::unspacify(expected_fgp_string); REQUIRE(document_string.toStdString() == expected_fgs_string.toStdString()); WHEN("Creating a FragmentationPathway only with PolChemDef") { FragmentationPathway fragmentation_pathway(pol_chem_def_csp); AND_WHEN("Initializing it with an XML element") { REQUIRE(fragmentation_pathway.renderXmlFgsElement( fragmentation_pathway_element, /*version*/ 1)); THEN( "The initalized FragmentationPathway is valid and validates " "successfully") { THEN("The FragmentationPathway is valid and validates successfully") { REQUIRE(fragmentation_pathway.isValid()); REQUIRE( fragmentation_pathway.validate(error_list_fragmentation_pathway)); REQUIRE(fragmentation_pathway.getName().toStdString() == "a"); REQUIRE(fragmentation_pathway.getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_pathway.getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1"); REQUIRE(fragmentation_pathway.getRulesCstRef().size() == 1); REQUIRE(fragmentation_pathway.getRulesCstRef() .at(0) ->getName() .toStdString() == "one_rule"); } } AND_WHEN("Exporting itself as a XML element") { QString xml_text = fragmentation_pathway.formatXmlFgpElement(/*offset*/ 1); xml_text = Utils::unspacify(xml_text); THEN("The text must be identical to the initial XML string") { REQUIRE(xml_text.toStdString() == expected_fgp_string.toStdString()); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_FragmentationRule.cpp000664 001750 001750 00000034354 15100504560 024001 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_frag_rule_1_letter("protein-1-letter", 1); TestUtils test_utils_frag_rule_3_letters("protein-3-letters", 1); ErrorList error_list_frag_rule; SCENARIO( "FragmentationRule objects can be constructed fully qualified in one go", "[FragmentationRule]") { test_utils_frag_rule_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; WHEN("Constructing a FragmentationRule with all valid parameters") { FragmentationRule frag_rule( pol_chem_def_csp, "a-fgr-2", "F", "D", "E", "thecomment", "+H100"); THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.isValid()); REQUIRE(frag_rule.validate(error_list_frag_rule)); } AND_THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE(frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } } } SCENARIO( "FragmentationRule objects can be constructed from a XML element", "[FragmentationRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; WHEN("Constructing a FragmentationRule with a valid XML element") { // Totally fake fgr !!! // // a-fgr-2 // +H100 // F // D // E // thecomment // QStringList dom_strings{"fgr", // 0 "name", "a-fgr-2", "formula", "+H100", "prev-mnm-code", "F", "curr-mnm-code", "D", "next-mnm-code", "E", "comment", "thecomment"}; QDomDocument document = test_utils_frag_rule_1_letter.craftFgrDomDocument(dom_strings); QDomElement frag_rule_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare // them. There must be a bug somewhere because with the original output, at // the screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); QString expected_string = "a-fgr-2+H100FDEthecomment"; expected_string = Utils::unspacify(expected_string); REQUIRE(document_string.toStdString() == expected_string.toStdString()); FragmentationRule frag_rule(pol_chem_def_csp, frag_rule_element, 1); THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.isValid()); REQUIRE(frag_rule.validate(error_list_frag_rule)); } AND_THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE(frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } } } SCENARIO( "FragmentationRule objects can be constructed empty and then initialized " "piecemeal " "until they are valid", "[FragmentationRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; WHEN("Constructing a FragmentationRule with no valid parameter") { FragmentationRule frag_rule; THEN("The FragmentationRule is invalid and does not validate successfully") { REQUIRE_FALSE(frag_rule.isValid()); REQUIRE_FALSE(frag_rule.validate(error_list_frag_rule)); } AND_WHEN( "Setting PolChemDef, a name, correct prev/curr/next codes, formula and " "comment") { frag_rule.setPolchemDefCstSPtr(pol_chem_def_csp); frag_rule.setName("a-fgr-2"); frag_rule.setPrevCode("F"); frag_rule.setCurrCode("D"); frag_rule.setNextCode("E"); frag_rule.setFormula(Formula("+H100")); frag_rule.setComment("thecomment"); THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.isValid()); REQUIRE(frag_rule.validate(error_list_frag_rule)); AND_THEN("All the members are set correctly") { REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE( frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } } } } } SCENARIO( "FragmentationRule objects can be initialized from an XML element", "[FragmentationRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; // Totally fake fgr !!! // // a-fgr-2 // +H100 // F // D // E // thecomment // QStringList dom_strings{"fgr", // 0 "name", "a-fgr-2", "formula", "+H100", "prev-mnm-code", "F", "curr-mnm-code", "D", "next-mnm-code", "E", "comment", "thecomment"}; QDomDocument document = test_utils_frag_rule_1_letter.craftFgrDomDocument(dom_strings); QDomElement frag_rule_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. // qDebug().noquote() << "The document:\n'" // << document.toString(/*indentation*/ 1) << "'"; // We need to remove all spaces from the strings to be able to compare // them. There must be a bug somewhere because with the original output, at // the screen everything seems correct, but test FAILED. QString document_string = document.toString(/*indentation*/ 0); document_string = Utils::unspacify(document_string); QString expected_string = "a-fgr-2+H100FDEthecomment"; expected_string = Utils::unspacify(expected_string); REQUIRE(document_string.toStdString() == expected_string.toStdString()); FragmentationRule frag_rule(pol_chem_def_csp, frag_rule_element, 1); WHEN( "A FragmentationRule instance is allocated entirely free of data (only the " "polymer chemistry definition is provided to the constructor)") { FragmentationRule frag_rule(pol_chem_def_csp, ""); THEN("The FragmentationRule instance is not valid and cannot validate") { REQUIRE(frag_rule.getName().toStdString() == ""); REQUIRE_FALSE(frag_rule.isValid()); REQUIRE_FALSE(frag_rule.validate(error_list_frag_rule)); AND_WHEN( "The FragmentationRule instance is asked to render in itself the " "XML " "element") { REQUIRE(frag_rule.renderXmlFgrElement(frag_rule_element)); AND_THEN("All the members are set correctly") { REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE( frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } } } } } SCENARIO( "FragmentationRule objects can output themselves to a XML element", "[FragmentationRule]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; QString expected_string = "a-fgr-2+H100FDEthecomment"; expected_string = Utils::unspacify(expected_string); GIVEN("Construction a FragmentationRule fully initialized") { FragmentationRule frag_rule( pol_chem_def_csp, "a-fgr-2", "F", "D", "E", "thecomment", "+H100"); THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.isValid()); REQUIRE(frag_rule.validate(error_list_frag_rule)); REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE(frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } WHEN("The FragmentationRule is exported as a XML element") { QString xml_text = frag_rule.formatXmlFgrElement(/*offset*/ 1); xml_text = Utils::unspacify(xml_text); THEN("The obtained text is as expected") { REQUIRE(xml_text.toStdString() == expected_string.toStdString()); } } } } SCENARIO( "FragmentationRule objects can be constructed by copy constructor or by " "assigment " "and then compared", "[FragmentationRule]") { test_utils_frag_rule_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_frag_rule_1_letter.msp_polChemDef; GIVEN("Construction a FragmentationRule fully initialized") { FragmentationRule frag_rule( pol_chem_def_csp, "a-fgr-2", "F", "D", "E", "thecomment", "+H100"); THEN("The FragmentationRule is valid and does validate successfully") { REQUIRE(frag_rule.isValid()); REQUIRE(frag_rule.validate(error_list_frag_rule)); REQUIRE(frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(frag_rule.getNextCode().toStdString() == "E"); REQUIRE(frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(frag_rule.getComment().toStdString() == "thecomment"); } WHEN("A new FragmentationRule is instantiated by copy-construction") { FragmentationRule new_frag_rule(frag_rule); THEN( "The new FragmentationRule is valid and does validate successfully and " "all " "the members are set correctly") { REQUIRE(new_frag_rule.isValid()); REQUIRE(new_frag_rule.validate(error_list_frag_rule)); REQUIRE(new_frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(new_frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(new_frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(new_frag_rule.getNextCode().toStdString() == "E"); REQUIRE( new_frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(new_frag_rule.getComment().toStdString() == "thecomment"); AND_THEN("The comparison operator do work properly") { REQUIRE(new_frag_rule == frag_rule); REQUIRE_FALSE(new_frag_rule != frag_rule); } } } WHEN("A new FragmentationRule is instantiated by assignment") { FragmentationRule new_frag_rule; new_frag_rule = frag_rule; THEN( "The new FragmentationRule is valid and does validate successfully and " "all " "the members are set correctly") { REQUIRE(new_frag_rule.isValid()); REQUIRE(new_frag_rule.validate(error_list_frag_rule)); REQUIRE(new_frag_rule.getName().toStdString() == "a-fgr-2"); REQUIRE(new_frag_rule.getPrevCode().toStdString() == "F"); REQUIRE(new_frag_rule.getCurrCode().toStdString() == "D"); REQUIRE(new_frag_rule.getNextCode().toStdString() == "E"); REQUIRE( new_frag_rule.getFormulaCstRef().getActionFormula().toStdString() == "+H100"); REQUIRE(new_frag_rule.getComment().toStdString() == "thecomment"); AND_THEN("The comparison operator do work properly") { REQUIRE(new_frag_rule == frag_rule); REQUIRE_FALSE(new_frag_rule != frag_rule); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Fragmenter.cpp000664 001750 001750 00000117031 15100504560 022437 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include #include #include #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_fragmenter("protein-1-letter", 1); ErrorList error_list_fragmenter; SCENARIO( "Construction of a Fragmenter and use with a Polymer without CrossLink " "instances with various ionization levelss and accounting of chemical " "entities", "[Fragmenter]") { test_utils_1_letter_fragmenter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_fragmenter.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 157); GIVEN( "No end fragmentation (immonium ion-like), monoprotonation, no accounting " "for chemical entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "imm", "-C1O1", Enums::FragEnd::NE, "immonium ion"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 1); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 25); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "imm#M#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "imm"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(104.0533955010, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(104.1943046945, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C4H10N1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "imm#K#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[25-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "imm"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(101.1078734279, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(101.1707707221, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C5H13N2"); } } } GIVEN( "No end fragmentation (immonium ion-like), monoprotonation, accounting for " "monomer chemical entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "imm", "-C1O1", Enums::FragEnd::NE, "immonium ion"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 1); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 25); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "imm#M#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "imm"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(104.0533955010, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(104.1943046945, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C4H10N1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().at(12); REQUIRE(oligomer_sp->getName().toStdString() == "imm#S#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[13-13]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "imm"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(140.0112697095, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(140.0553408372, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C2H7N1O4P1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "imm#K#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[25-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "imm"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(101.1078734279, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(101.1707707221, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C5H13N2"); } } } GIVEN( "Left end fragmentation, monoprotonation, no accounting for chemical " "entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "a", "-C1O1H1", Enums::FragEnd::LE, "fragment a"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 1); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 24); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "a#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(104.0533955010, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(104.1943046945, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C4H10N1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "a#24#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-24]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(2307.0895472739, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(2308.6468918738, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C93H160N29O33S3"); } } } GIVEN( "Left end fragmentation, multiprotonation, no accounting for chemical " "entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "a", "-C1O1H1", Enums::FragEnd::LE, "fragment a"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 48); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "a#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(104.0533955010, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(104.1943046945, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C4H10N1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "a#24#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-24]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1154.0486861531, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1154.8274166711, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C93H161N29O33S3"); } } } GIVEN("Left end fragmentation, multiprotonation, Enums::ChemicalEntity::MODIF") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "a", "-C1O1H1", Enums::FragEnd::LE, "fragment a"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 48); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "a#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(104.0533955010, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(104.1943046945, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C4H10N1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "a#24#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-24]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1194.0318515988, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1194.8173814803, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C93H162N29O36S3P1"); } } } GIVEN( "Left end fragmentation, multiprotonation, Enums::ChemicalEntity::MODIF and " "Enums::ChemicalEntity::LEFT_END_MODIF") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "a", "-C1O1H1", Enums::FragEnd::LE, "fragment a"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::LEFT_END_MODIF); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 48); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "a#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(146.0639601857, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(146.2312448490, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C6H12N1O1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "a#24#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-24]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1215.0371339412, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1215.8358515576, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C95H164N29O37S3P1"); } } } GIVEN( "Left end fragmentation, multiprotonation, Enums::ChemicalEntity::MODIF and " "Enums::ChemicalEntity::LEFT_END_MODIF with added -H2O and -NH3 additinal " "decompositions") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "a", "-C1O1H1", Enums::FragEnd::LE, "fragment a"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 1); REQUIRE(fragmentation_config.addFormula("-H2O")); REQUIRE(fragmentation_config.addFormula("-NH3")); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::LEFT_END_MODIF); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { qDebug() << "BEGIN HERE"; REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 72); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "a#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-1]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(146.0639601857, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(146.2312448490, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C6H12N1O1S1"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "a#24#-NH3#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[1-24]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "a"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(2412.0398937491, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(2413.6332320508, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C95H160N28O37S3P1"); } } } GIVEN( "Right end fragmentation, monoprotonation, no accounting for chemical " "entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "x", "+C1O1-H1", Enums::FragEnd::RE, "fragment x"); // Fragmented peptide [0]MAMISGMSGRKASGSSPTSPINADK[24] std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 1); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 24); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "x#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[25-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(173.0926172885, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(173.1906453741, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C7H13N2O3"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "x#24#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[2-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(2376.1287690614, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(2377.6432325534, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C96H163N30O36S2"); } } } GIVEN( "Right end fragmentation, multiprotonation, no accounting for chemical " "entities") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "x", "+C1O1-H1", Enums::FragEnd::RE, "fragment x"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::NONE, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 48); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "x#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[25-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(173.0926172885, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(173.1906453741, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C7H13N2O3"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "x#24#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[2-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1188.5682970468, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1189.3255870109, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C96H164N30O36S2"); } } } GIVEN("Right end fragmentation, multiprotonation, Enums::ChemicalEntity::MODIF") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "x", "+C1O1-H1", Enums::FragEnd::RE, "fragment x"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 0, /*stop index*/ 24); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::NONE); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 48); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "x#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[25-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(173.0926172885, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(173.1906453741, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C7H13N2O3"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "x#24#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[2-25]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(1228.5514624926, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(1229.3155518201, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C96H165N30O39S2P1"); } } } GIVEN( "Right end fragmentation, multiprotonation, Enums::ChemicalEntity::MODIF and " "Enums::ChemicalEntity::RIGHT_END_MODIF") { FragmentationPathway fragmentation_pathway( polymer_sp->getPolChemDefCstSPtr(), "x", "+C1O1-H1", Enums::FragEnd::RE, "fragment x"); std::vector fragmentation_configs; FragmentationConfig fragmentation_config(fragmentation_pathway, /*start index*/ 140, /*stop index*/ 156); fragmentation_config.setIonizeLevels(1, 2); fragmentation_configs.push_back(fragmentation_config); CalcOptions calc_options(/*deep_calculation*/ false, Enums::MassType::BOTH, Enums::CapType::BOTH, Enums::ChemicalEntity::MODIF, Enums::ChemicalEntity::RIGHT_END_MODIF); calc_options.setIndexRange(0, 239); Ionizer protonation( polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Formula("+H"), 1, 1); Fragmenter fragmenter(polymer_sp, polymer_sp->getPolChemDefCstSPtr(), fragmentation_configs, calc_options, protonation); WHEN("The fragmentation is asked for") { REQUIRE(fragmenter.fragment()); THEN( "There should be the right number of oligomers and these should as " "expected") { REQUIRE(fragmenter.getOligomerCollectionCstRef().size() == 32); OligomerSPtr oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().front(); REQUIRE(oligomer_sp->getName().toStdString() == "x#1#z=1"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[157-157]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(173.0562317796, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(173.1474639674, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C6H9N2O4"); oligomer_sp = fragmenter.getOligomerCollectionCstRef().getOligomersCstRef().back(); REQUIRE(oligomer_sp->getName().toStdString() == "x#16#z=2"); REQUIRE(oligomer_sp->getCalcOptionsCstRef() .getIndexRangeCollectionCstRef() .positionsAsText() .toStdString() == "[142-157]"); REQUIRE(oligomer_sp->getDescription().toStdString() == "x"); REQUIRE_FALSE(oligomer_sp->isModified()); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(903.8013928149, 0.0000000001)); REQUIRE_THAT(oligomer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(904.2980585499, 0.0000000001)); REQUIRE( oligomer_sp->getFormulaCstRef().getActionFormula().toStdString() == "C68H97N17O41"); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_IndexRangeCollection.cpp000664 001750 001750 00000065602 15100504560 024413 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("Construction of a IndexRangeCollection instance", "[IndexRangeCollection]") { QString index_range_string_1("[50-150]"); IndexRange index_range_1(50, 150); QString index_range_string_2("[40-160]"); IndexRange index_range_2(40, 160); QString index_range_string_3("[30-170]"); IndexRange index_range_3(30, 170); QString all_three_index_range_strings = QString("%1%2%3") .arg(index_range_string_1) .arg(index_range_string_2) .arg(index_range_string_3); GIVEN("Construction with no argument") { IndexRangeCollection index_range_collection; WHEN("A comment is set to it") { index_range_collection.setComment("Comment"); THEN("The comment should be set") { REQUIRE(index_range_collection.getComment().toStdString() == "Comment"); } } } GIVEN( "Construction of a IndexRangeCollection instance with a string representing " "three IndexRange instances") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list must have size 3") { REQUIRE(index_range_collection.size() == 3); AND_THEN("All the members of the list should have proper values") { IndexRange first_index_range = index_range_collection.getRangeCstRefAt(0); REQUIRE(first_index_range.start == index_range_1.start - 1); REQUIRE(first_index_range.stop == index_range_1.stop - 1); IndexRange second_index_range = index_range_collection.getRangeCstRefAt(1); REQUIRE(second_index_range.start == index_range_2.start - 1); REQUIRE(second_index_range.stop == index_range_2.stop - 1); IndexRange third_index_range = index_range_collection.getRangeCstRefAt(2); REQUIRE(third_index_range.start == index_range_3.start - 1); REQUIRE(third_index_range.stop == index_range_3.stop - 1); } } } GIVEN( "An incorrect string incorrectly representing three IndexRange (missing " "1 " "'[')") { QString all_three_index_range_strings("50-150][40-160][30-170]"); WHEN("Constructing a IndexRangeCollection instance") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list is created empty") { REQUIRE(index_range_collection.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three IndexRange (missing " "1 " "']'") { QString all_three_index_range_strings("[50-150][40-160[30-170]"); WHEN("Constructing a IndexRangeCollection instance") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list is created empty") { REQUIRE(index_range_collection.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three IndexRange (missing " "1 " "']' and 1 '['") { QString all_three_index_range_strings("50-150][40-160[30-170]"); WHEN("Constructing a IndexRangeCollection instance") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list is created empty") { REQUIRE(index_range_collection.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three IndexRange (missing " "1 " "'-'") { QString all_three_index_range_strings("[50-150][40-160][30 170]"); WHEN("Constructing a IndexRangeCollection instance") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list is created empty") { REQUIRE(index_range_collection.size() == 0); } } } GIVEN("Construction with a comment and no list; and IndexRange objects") { IndexRangeCollection index_range_collection; index_range_collection.setComment("Comment"); REQUIRE(index_range_collection.getComment().toStdString() == "Comment"); REQUIRE(index_range_collection.size() == 0); WHEN("One IndexRange object is appended to the list") { index_range_collection.appendIndexRange(index_range_1); THEN( "The list must have size 1, and that object has to be the one appended") { REQUIRE(index_range_collection.size() == 1); REQUIRE(index_range_collection.getRangeCstRefAt(0) == index_range_1); } AND_WHEN("Another IndexRange object is appended to the list") { index_range_collection.appendIndexRange(index_range_2); THEN( "The list must have size 2, and that object has to be the last of " "the list") { REQUIRE(index_range_collection.size() == 2); REQUIRE(index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1) == index_range_2); } AND_WHEN( "That 2-sized IndexRangeCollection instance is copied (copy-constructor " "with reference)") { IndexRangeCollection new_index_range_collection(index_range_collection); THEN("The two IndexRangeCollection instances must have the same data") { REQUIRE(new_index_range_collection.getComment().toStdString() == index_range_collection.getComment().toStdString()); REQUIRE(new_index_range_collection.size() == index_range_collection.size()); REQUIRE(new_index_range_collection.getRangeCstRefAt(0) == index_range_collection.getRangeCstRefAt(0)); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) == index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1)); } } AND_WHEN( "That 2-sized IndexRangeCollection instance is copied (assignment " "operator) to itself, a reference to itself is returned") { REQUIRE((index_range_collection = index_range_collection) == index_range_collection); } AND_WHEN( "That 2-sized IndexRangeCollection instance is copied (assignment " "operator)") { IndexRangeCollection new_index_range_collection; new_index_range_collection = index_range_collection; THEN("The two IndexRangeCollection instances must have the same data") { REQUIRE(new_index_range_collection.getComment().toStdString() == index_range_collection.getComment().toStdString()); REQUIRE(new_index_range_collection.size() == index_range_collection.size()); REQUIRE(new_index_range_collection.getRangeCstRefAt(0) == index_range_collection.getRangeCstRefAt(0)); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) == index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1)); } } } } } } SCENARIO( "Empty IndexRangeCollection instances can be filled-in after construction with " "other IndexRangeCollection", "[IndexRangeCollection]") { GIVEN( "Construction of a IndexRangeCollection instance with two IndexRange " "instances") { IndexRange index_range_1(50, 150); IndexRange index_range_2(40, 160); IndexRange index_range_3(30, 170); IndexRangeCollection index_range_collection_0; index_range_collection_0.setComment("Giver"); index_range_collection_0.appendIndexRange(index_range_1); index_range_collection_0.appendIndexRange(index_range_2); REQUIRE(index_range_collection_0.size() == 2); REQUIRE(index_range_collection_0.getRangeCstRefAt(0) == index_range_1); REQUIRE(index_range_collection_0.getRangeCstRefAt(index_range_collection_0.size() - 1) == index_range_2); AND_GIVEN("Construction of an empty IndexRangeCollection instance") { IndexRangeCollection index_range_collection; index_range_collection.setComment("Receiver"); REQUIRE(index_range_collection.getComment().toStdString() == "Receiver"); REQUIRE(index_range_collection.size() == 0); WHEN( "The first IndexRangeCollection instance with two items is appended to the " "the empty one") { index_range_collection.appendIndexRanges(index_range_collection_0); THEN("Its size must become the same at the one that was appended") { REQUIRE(index_range_collection.size() == index_range_collection_0.size()); REQUIRE(index_range_collection.getRangeCstRefAt(0) == index_range_collection_0.getRangeCstRefAt(0)); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1) == index_range_collection_0.getRangeCstRefAt(index_range_collection_0.size() - 1)); } AND_WHEN("A new IndexRange instance is set (not appended)") { index_range_collection.setIndexRange(index_range_3); THEN("The single instance has to be this last") { REQUIRE(index_range_collection.size() == 1); REQUIRE(index_range_collection.getRangeCstRefAt(0) == index_range_3); } AND_WHEN("A IndexRangeCollection instance is set (not appended)") { index_range_collection.setIndexRanges(index_range_collection_0); THEN( "The previous single instance has to be lost and be replaced by " "the new ones") { REQUIRE(index_range_collection.size() == 2); REQUIRE(index_range_collection.getRangeCstRefAt(0) == index_range_collection_0.getRangeCstRefAt(0)); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1) == index_range_collection_0.getRangeCstRefAt(index_range_collection_0.size() - 1)); } } } } } } } SCENARIO( "Empty IndexRangeCollection instances can be filled in after construction with " "strings representative of IndexRange instances (single or multiple)", "[IndexRangeCollection]") { QString index_range_string_1("[50-150]"); IndexRange index_range_1(50, 150); QString index_range_string_2("[40-160]"); IndexRange index_range_2(40, 160); QString index_range_string_3("[30-170]"); IndexRange index_range_3(30, 170); QString all_three_index_range_strings = QString("%1%2%3") .arg(index_range_string_1) .arg(index_range_string_2) .arg(index_range_string_3); GIVEN("Construction with a comment and no list; and IndexRange objects") { IndexRangeCollection index_range_collection; index_range_collection.setComment("Comment"); REQUIRE(index_range_collection.getComment().toStdString() == "Comment"); REQUIRE(index_range_collection.size() == 0); WHEN( "That IndexRangeCollection is set with one string representative of a single " "IndexRange instance") { index_range_collection.setIndexRanges(index_range_string_1); THEN( "The list must have size 1, and that object has to be the one set, " "with each first/second value decremented by one to convert positions " "to " "indices") { REQUIRE(index_range_collection.size() == 1); REQUIRE(index_range_collection.getRangeCstRefAt(0).start == index_range_1.start - 1); REQUIRE(index_range_collection.getRangeCstRefAt(0).stop == index_range_1.stop - 1); } } WHEN( "That IndexRangeCollection is set with one string representative of three " "IndexRange instances") { index_range_collection.setIndexRanges(all_three_index_range_strings); THEN("The list must have size 3") { REQUIRE(index_range_collection.size() == 3); } AND_THEN("All the members of the list should have proper values") { REQUIRE(index_range_collection.getRangeCstRefAt(0).start == index_range_1.start - 1); REQUIRE(index_range_collection.getRangeCstRefAt(0).stop == index_range_1.stop - 1); REQUIRE(index_range_collection.getRangeCstRefAt(1).start == index_range_2.start - 1); REQUIRE(index_range_collection.getRangeCstRefAt(1).stop == index_range_2.stop - 1); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1).start == index_range_3.start - 1); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1).stop == index_range_3.stop - 1); } } } } SCENARIO("IndexRangeCollection instances can provide topological data", "[IndexRangeCollection]") { GIVEN("A IndexRangeCollection instance with a single IndexRange instance") { IndexRangeCollection index_range_collection; QString all_index_range_strings("[1-20]"); index_range_collection.setIndexRanges(all_index_range_strings); REQUIRE(index_range_collection.size() == 1); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost IndexRange instances") { left_most_indices = index_range_collection.indicesOfLeftMostIndexRanges(); right_most_indices = index_range_collection.indicesOfRightMostIndexRanges(); REQUIRE(left_most_indices.size() == 1); REQUIRE(right_most_indices.size() == 1); THEN( "The indices should actually match the correct IndexRange instance") { REQUIRE( index_range_collection.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( index_range_collection.getRangeCstRefAt(right_most_indices.at(0)).stop == 19); } } WHEN( "Checking if there are overlaps, there cannot be because only one " "instance") { REQUIRE_FALSE(index_range_collection.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(index_range_collection.encompassIndex(0)); REQUIRE(index_range_collection.encompassIndex(19)); REQUIRE(index_range_collection.encompassIndex(10)); REQUIRE_FALSE(index_range_collection.encompassIndex(20)); } } WHEN("Checking for indices left most IndexRange") { THEN("The results should be consistent") { REQUIRE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(0))); REQUIRE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(0))); } } } GIVEN( "A IndexRangeCollection instance with a number of non-overlapping IndexRange") { IndexRangeCollection index_range_collection; QString all_index_range_strings("[1-20][25-45][55-100]"); index_range_collection.setIndexRanges(all_index_range_strings); REQUIRE(index_range_collection.size() == 3); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost IndexRangeCollection") { left_most_indices = index_range_collection.indicesOfLeftMostIndexRanges(); right_most_indices = index_range_collection.indicesOfRightMostIndexRanges(); REQUIRE(left_most_indices.size() == 1); REQUIRE(right_most_indices.size() == 1); THEN( "The indices should actually match the right IndexRange instances") { REQUIRE( index_range_collection.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( index_range_collection.getRangeCstRefAt(right_most_indices.at(0)).stop == 99); } } WHEN("Checking if there are overlaps, there should not be") { REQUIRE_FALSE(index_range_collection.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(index_range_collection.encompassIndex(0)); REQUIRE_FALSE(index_range_collection.encompassIndex(50)); REQUIRE(index_range_collection.encompassIndex(50, /*globally*/ true)); REQUIRE(index_range_collection.encompassIndex(99)); REQUIRE_FALSE(index_range_collection.encompassIndex(100)); } } WHEN("Checking for indices left most IndexRange") { THEN("The results should be consistent") { // "[1-20][25-45][55-100]" REQUIRE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(0))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(0))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(1))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(1))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(2))); REQUIRE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(2))); } } } GIVEN( "A IndexRangeCollection instance with a number of IndexRange with overlaps") { IndexRangeCollection index_range_collection; QString all_index_range_strings( "[1-20][1-30][10-50][15-80][25-100][30-100]"); index_range_collection.setIndexRanges(all_index_range_strings); REQUIRE(index_range_collection.size() == 6); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost IndexRangeCollection") { left_most_indices = index_range_collection.indicesOfLeftMostIndexRanges(); right_most_indices = index_range_collection.indicesOfRightMostIndexRanges(); REQUIRE(left_most_indices.size() == 2); REQUIRE(right_most_indices.size() == 2); THEN( "The indices should actually match the right IndexRange instances") { REQUIRE( index_range_collection.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( index_range_collection.getRangeCstRefAt(left_most_indices.back()).start == 0); REQUIRE( index_range_collection.getRangeCstRefAt(right_most_indices.at(0)).stop == 99); REQUIRE( index_range_collection.getRangeCstRefAt(right_most_indices.back()).stop == 99); } } WHEN("Checking if there are overlaps, there should be") { REQUIRE(index_range_collection.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(index_range_collection.encompassIndex(0)); REQUIRE(index_range_collection.encompassIndex(49)); REQUIRE(index_range_collection.encompassIndex(50)); REQUIRE(index_range_collection.encompassIndex(99)); REQUIRE_FALSE(index_range_collection.encompassIndex(100)); } } WHEN("Checking for indices left most IndexRange") { THEN("The results should be consistent") { // "[1-20][1-30][10-50][15-80][25-100][30-100]" REQUIRE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(0))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(0))); REQUIRE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(1))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(1))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(2))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(2))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(3))); REQUIRE_FALSE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(3))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(4))); REQUIRE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(4))); REQUIRE_FALSE(index_range_collection.isLeftMostIndexRange( index_range_collection.getRangeCstRefAt(5))); REQUIRE(index_range_collection.isRightMostIndexRange( index_range_collection.getRangeCstRefAt(5))); } } } } SCENARIO("IndexRangeCollection instances can self-describe with text strings", "[IndexRangeCollection]") { QString all_three_index_range_strings("[50-150][40-160][30-170]"); GIVEN("Construction with a string describing three IndexRange instances") { IndexRangeCollection index_range_collection(all_three_index_range_strings); WHEN("When asked to self-describe as indices") { QString text = index_range_collection.indicesAsText(); THEN("The string should be list indices and not positions") { REQUIRE(text.toStdString() == "[49-149][39-159][29-169]"); } } WHEN("When asked to self-describe as positions") { QString text = index_range_collection.positionsAsText(); THEN("The string should be list positions and not indices") { REQUIRE(text.toStdString() == all_three_index_range_strings.toStdString()); } } } } SCENARIO("IndexRangeCollection and IndexRange instances can be compared", "[IndexRangeCollection]") { QString index_range_string_1("[50-150]"); IndexRange index_range_1(50, 150); QString index_range_string_2("[40-160]"); IndexRange index_range_2(40, 160); QString index_range_string_3("[30-170]"); IndexRange index_range_3(30, 170); QString all_three_index_range_strings = QString("%1%2%3") .arg(index_range_string_1) .arg(index_range_string_2) .arg(index_range_string_3); GIVEN("A IndexRangeCollection instance with 3 IndexRange instances") { IndexRangeCollection index_range_collection(all_three_index_range_strings); THEN("The list must have size 3") { REQUIRE(index_range_collection.size() == 3); } AND_THEN("All the members of the list should have proper values") { REQUIRE(index_range_collection.getRangeCstRefAt(0).start == index_range_1.start - 1); REQUIRE(index_range_collection.getRangeCstRefAt(0).stop == index_range_1.stop - 1); REQUIRE(index_range_collection.getRangeCstRefAt(1).start == index_range_2.start - 1); REQUIRE(index_range_collection.getRangeCstRefAt(1).stop == index_range_2.stop - 1); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1).start == index_range_3.start - 1); REQUIRE( index_range_collection.getRangeCstRefAt(index_range_collection.size() - 1).stop == index_range_3.stop - 1); } WHEN("A new IndexRangeCollection instance is created as a copy of the first one") { IndexRangeCollection new_index_range_collection(index_range_collection); THEN("The new instance should be identical to the initial one") { REQUIRE(new_index_range_collection.size() == 3); REQUIRE(new_index_range_collection.getRangeCstRefAt(0).start == index_range_1.start - 1); REQUIRE(new_index_range_collection.getRangeCstRefAt(0).stop == index_range_1.stop - 1); REQUIRE(new_index_range_collection.getRangeCstRefAt(1).start == index_range_2.start - 1); REQUIRE(new_index_range_collection.getRangeCstRefAt(1).stop == index_range_2.stop - 1); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) .start == index_range_3.start - 1); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) .stop == index_range_3.stop - 1); } AND_THEN("The comparison operators should return identity") { REQUIRE(new_index_range_collection == index_range_collection); REQUIRE_FALSE(new_index_range_collection != index_range_collection); } AND_WHEN("A one IndexRange instance is modified") { // IndexRange index_range_1(50, 150); // IndexRange index_range_2(40, 160); // IndexRange index_range_3(30, 170); IndexRangeCollection new_index_range_collection(index_range_collection); IndexRange &index_range = new_index_range_collection.getRangeRefAt(1); index_range.start = 50; index_range.stop = 150; THEN("The new instance should not be identical anymore") { REQUIRE(new_index_range_collection.size() == 3); REQUIRE(new_index_range_collection.getRangeCstRefAt(0).start == index_range_1.start - 1); REQUIRE(new_index_range_collection.getRangeCstRefAt(0).stop == index_range_1.stop - 1); REQUIRE(new_index_range_collection.getRangeCstRefAt(1).start == 50); REQUIRE(new_index_range_collection.getRangeCstRefAt(1).stop == 150); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) .start == index_range_3.start - 1); REQUIRE( new_index_range_collection.getRangeCstRefAt(new_index_range_collection.size() - 1) .stop == index_range_3.stop - 1); } AND_THEN("The comparison operators should return identity") { REQUIRE(new_index_range_collection != index_range_collection); REQUIRE_FALSE(new_index_range_collection == index_range_collection); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Ionizer.cpp000664 001750 001750 00000040065 15100504560 021766 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include #include #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_ionizer("protein-1-letter", 1); SCENARIO("An Ionizer is used to attach charges to chemical entities", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; isotopic_data_lib_handler.setFileName("the_file_name.dat"); std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = isotopic_data_lib_handler.getIsotopicData(); REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("Construction of an empty Ionizer") { Ionizer ionizer; THEN( "The member data are set to emtpy/0/false and the Ionizer is *not* valid") { REQUIRE(ionizer.getIsotopicDataCstSPtr() == nullptr); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == ""); REQUIRE(ionizer.getNominalCharge() == 0); REQUIRE(ionizer.getLevel() == 0); REQUIRE(ionizer.charge() == 0); REQUIRE(ionizer.isValid() == false); } AND_GIVEN("Configuring MALDI-type ionization with setters") { ionizer.setIsotopicDataCstSPtr(isotopic_data_csp); ionizer.setFormula("\"Protonation\"+H"); ionizer.setNominalCharge(1); ionizer.setLevel(1); THEN("Member data are updated accordingly") { REQUIRE(ionizer.getIsotopicDataCstSPtr() == isotopic_data_csp); REQUIRE(ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(ionizer.getNominalCharge() == 1); REQUIRE(ionizer.getLevel() == 1); } GIVEN( "Construction of a new copy of that Ionizer by the copy constructor") { Ionizer new_ionizer(ionizer); THEN("The members should be identical to the former Ionizer") { REQUIRE(new_ionizer.getIsotopicDataCstSPtr() == isotopic_data_csp); REQUIRE(new_ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE( new_ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(new_ionizer.getNominalCharge() == 1); REQUIRE(new_ionizer.getLevel() == 1); } AND_THEN("Checking for (in)equality should work") { REQUIRE(ionizer == ionizer); REQUIRE_FALSE(ionizer != ionizer); REQUIRE(new_ionizer == ionizer); REQUIRE_FALSE(new_ionizer != ionizer); } } GIVEN( "Construction of a new copy of that Ionizer using data from the " "first one") { Ionizer new_ionizer(ionizer.getIsotopicDataCstSPtr(), ionizer.getFormulaCstRef(), ionizer.getNominalCharge(), ionizer.getLevel()); THEN("The members should be identical to the former Ionizer") { REQUIRE(new_ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE( new_ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(new_ionizer.getNominalCharge() == 1); REQUIRE(new_ionizer.getLevel() == 1); } AND_THEN("Checking for (in)equality should work") { REQUIRE((ionizer = ionizer) == ionizer); REQUIRE(ionizer == ionizer); REQUIRE_FALSE(ionizer != ionizer); REQUIRE(new_ionizer == ionizer); REQUIRE_FALSE(new_ionizer != ionizer); } } GIVEN("Construction of a new copy of that Ionizer by assignment") { Ionizer new_ionizer; new_ionizer = ionizer; THEN("The members should be identical to the former Ionizer") { REQUIRE(new_ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE( new_ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(new_ionizer.getNominalCharge() == 1); REQUIRE(new_ionizer.getLevel() == 1); } AND_THEN("Checking for (in)equality should work") { REQUIRE(new_ionizer == ionizer); REQUIRE_FALSE(new_ionizer != ionizer); REQUIRE(new_ionizer == ionizer); REQUIRE_FALSE(new_ionizer != ionizer); } } } } } SCENARIO( "An Ionizer correctly initialized should validate against reference data", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; isotopic_data_lib_handler.setFileName("the_file_name.dat"); std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = isotopic_data_lib_handler.getIsotopicData(); REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("An empty Ionizer") { Ionizer ionizer; WHEN("That Ionizer is initialized without IsotopicData") { ionizer.setFormula("\"Protonation\"+H"); ionizer.setNominalCharge(1); ionizer.setLevel(1); THEN("Its member data are updated") { REQUIRE(ionizer.getIsotopicDataCstSPtr() == nullptr); REQUIRE(ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(ionizer.getNominalCharge() == 1); REQUIRE(ionizer.getLevel() == 1); } AND_THEN("Without IsotopicData the Ionizer is not valid") { ErrorList error_list; REQUIRE_FALSE(ionizer.validate(&error_list)); REQUIRE_FALSE(ionizer.isValid()); } AND_WHEN("The IsotopicData are set") { ionizer.setIsotopicDataCstSPtr(isotopic_data_csp); THEN("Without IsotopicData the Ionizer is not valid") { ErrorList error_list; REQUIRE(ionizer.validate(&error_list)); REQUIRE(ionizer.isValid()); } } } } } SCENARIO("An Ionizer can output a text string describing itself", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; isotopic_data_lib_handler.setFileName("the_file_name.dat"); std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = isotopic_data_lib_handler.getIsotopicData(); REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("An empty Ionizer") { Ionizer ionizer; WHEN("That Ionizer is fully initialized") { ionizer.setIsotopicDataCstSPtr(isotopic_data_csp); ionizer.setFormula("\"Protonation\"+H"); ionizer.setNominalCharge(1); ionizer.setLevel(1); AND_WHEN("The Ionizer is output as a string without the title") { QString expected_string = "Ionizer destination state. formula: +H - nominal " "charge: 1 - " "level: 1 - is valid: 1\n" "Ionizer current state. formula: - nominal charge: 0 - level: 0 - " "charge: 0 - " "was valid: 1\n"; QString output = ionizer.toString(/*with_title*/ false); THEN("That string is tested") { REQUIRE(output.toStdString() ==expected_string.toStdString()); } } AND_WHEN("The Ionizer is output as a string with the title") { QString output = ionizer.toString(/*with_title*/ true); THEN("That string is tested") { QString expected_string = "Ionizer destination state. formula: \"Protonation\"+H - nominal " "charge: 1 - " "level: 1 - is valid: 1\n" "Ionizer current state. formula: - nominal charge: 0 - level: 0 - " "charge: 0 - " "was valid: 1\n"; REQUIRE(output.toStdString() == expected_string.toStdString()); } } } } } SCENARIO( "An Ionizer can be initialized with an XML element if it has IsotopicData " "available", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; isotopic_data_lib_handler.setFileName("the_file_name.dat"); std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = isotopic_data_lib_handler.getIsotopicData(); REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); // int ionizerule_element_index = 0; // int formula_element_index = 1; // int formula_text_index = 2; // int charge_element_index = 3; // int charge_text_index = 4; // int level_element_index = 5; // int level_text_index = 6; QStringList dom_strings{ "ionizerule", "formula", "\"Protonation\"+H", "charge", "1", "level", "1"}; QDomDocument document = test_utils_1_letter_ionizer.craftIonizeruleDomDocument(dom_strings); QDomElement ionizerule_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); WHEN( "An empty Ionizer instance is initialized by rendering in it the XML " "element") { Ionizer ionizer(isotopic_data_csp); qDebug() << "Now rendering the XML element."; REQUIRE(ionizer.renderXmlIonizeRuleElement(ionizerule_element) == true); THEN("Its member data are initialized") { REQUIRE(ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(ionizer.getNominalCharge() == 1); REQUIRE(ionizer.getLevel() == 1); } WHEN("The Ionizer is output as an XML element string") { QString output_xml_string = ionizer.formatXmlIonizeRuleElement(0, Utils::xmlIndentationToken); QString theoretically_expected = ""; theoretically_expected += "\n"; theoretically_expected += " \"Protonation\"+H\n"; theoretically_expected += " 1\n"; theoretically_expected += " 1\n"; theoretically_expected += "\n"; THEN("That string matches the expected string") { REQUIRE(output_xml_string.toStdString() == theoretically_expected.toStdString()); } } } } SCENARIO("An Ionizer documents itself as an XML element", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; isotopic_data_lib_handler.setFileName("the_file_name.dat"); std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); IsotopicDataCstSPtr isotopic_data_csp = isotopic_data_lib_handler.getIsotopicData(); REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_csp->size() == loaded_isotope_count); GIVEN("A fully initialized Ionizer") { Ionizer ionizer(isotopic_data_csp, "\"Protonation\"+H", 1, 1); THEN("Its member data are updated") { REQUIRE(ionizer.getFormulaCstRef().getTitle().toStdString() == "Protonation"); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(ionizer.getNominalCharge() == 1); REQUIRE(ionizer.getLevel() == 1); REQUIRE(ionizer.charge() == 1); } WHEN("The Ionizer is output as an XML element string") { QString output_xml_string = ionizer.formatXmlIonizeRuleElement(0, " "); QString theoretically_expected = ""; theoretically_expected += "\n"; theoretically_expected += " \"Protonation\"+H\n"; theoretically_expected += " 1\n"; theoretically_expected += " 1\n"; theoretically_expected += "\n"; THEN("That string matches the expected string") { REQUIRE(output_xml_string.toStdString() == theoretically_expected.toStdString()); } } } } SCENARIO("An Ionizer ionizes an unionized analyte", "[Ionizer]") { test_utils_1_letter_ionizer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_ionizer.msp_polChemDef; IsotopicDataCstSPtr isotopic_data_csp = pol_chem_def_csp->getIsotopicDataCstSPtr(); REQUIRE(pol_chem_def_csp->getCodeLength() == 1); REQUIRE(pol_chem_def_csp->getMonomersCstRef().size() == 21); REQUIRE(pol_chem_def_csp->getModifsCstRef().size() == 26); // REQUIRE(pol_chem_def_csp->crossLinkerList().size() == 2); // REQUIRE(pol_chem_def_csp->cleaveSpecList().size() == 8); // REQUIRE(pol_chem_def_csp->fragSpecList().size() == 7); ErrorList error_list; GIVEN( "A fully initialized Ionizer and initial masses for an unionized analyte") { Ionizer mono_protonator(isotopic_data_csp, "\"Protonation\"+H", 1, 1); REQUIRE(mono_protonator.validate(&error_list)); double analyte_mono = 17325.7923591224; double analyte_avg = 17336.8283764289; WHEN("The analyte is monoprotonated") { REQUIRE(mono_protonator.ionize(analyte_mono, analyte_avg) == Enums::IonizationOutcome::IONIZED); THEN("The masses become m/z values") { REQUIRE_THAT( analyte_mono, Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( analyte_avg, Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); AND_THEN("The analyte has to be ionized") { REQUIRE(mono_protonator.isIonized()); } } AND_WHEN("The analyte is deionized") { REQUIRE(mono_protonator.deionize(analyte_mono, analyte_avg) == Enums::IonizationOutcome::DEIONIZED); THEN("The masses become molecular masses again") { REQUIRE_THAT( analyte_mono, Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( analyte_avg, Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); AND_THEN("The analyte has to be deionized") { REQUIRE_FALSE(mono_protonator.isIonized()); } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Isotope.cpp000664 001750 001750 00000060674 15100504560 022001 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include /////////////////////// Local includes #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("Isotope construction with correct arguments", "[Isotope]") { TestUtils test_utils; QVector error_list; test_utils.initializeXpertmassLibrary(); WHEN("An isotope is constructed with fully descriptive arguments") { Isotope isotope_1( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("All the member data are set accordingly") { // qDebug() << "The isotope string is: " << // isotope_1.toString().toStdString(); REQUIRE(isotope_1.getName().toStdString() == "carbon"); REQUIRE(isotope_1.getSymbol().toStdString() == "C"); REQUIRE(isotope_1.getMass() == 12.0); REQUIRE(isotope_1.getProbability() == 0.989211941850466902614869013632414862513542175292968750000000); AND_THEN("The Isotope should both be valid and validate successfully") { REQUIRE(isotope_1.isValid()); REQUIRE(isotope_1.validate(&error_list)); REQUIRE(error_list_p->size() == 0); } WHEN("A new Isotope is assignment operator constructed") { Isotope new_isotope; new_isotope = isotope_1; THEN("The new instance should be identical to the first one") { REQUIRE(new_isotope == isotope_1); REQUIRE(new_isotope.validate(&error_list)); REQUIRE(error_list_p->size() == 0); } THEN("An Isotope instance == compared to itself should return true") { REQUIRE((isotope_1 == isotope_1)); } THEN("An Isotope instance != compared to itself should return false") { REQUIRE_FALSE((isotope_1 != isotope_1)); } } WHEN( "An Isotope is assigned to itself, a reference to itself is returned") { REQUIRE((isotope_1 = isotope_1) == isotope_1); } } AND_WHEN( "An identical isotope is constructed with an identical textual " "representation") { QString isotope_string(" carbon, C, 12.0, "); isotope_string += "0.989211941850466902614869013632414862513542175292968750000000"; // qDebug() << "The isotope_string:" << isotope_string.toStdString(); Isotope isotope_2(isotope_string); THEN("All the member data are set accordingly") { // qDebug() << "The isotope string is: " << // isotope_1.toString().toStdString(); REQUIRE(isotope_2.getName() == "carbon"); REQUIRE(isotope_2.getSymbol() == "C"); REQUIRE(isotope_2.getMass() == 12.0); REQUIRE(isotope_2.getProbability() == 0.989211941850466902614869013632414862513542175292968750000000); AND_THEN("The Isotope should both be valid and validate successfully") { REQUIRE(isotope_2.isValid()); QVector error_list; REQUIRE(isotope_2.validate(&error_list)); REQUIRE(error_list_p->size() == 0); } AND_THEN("Both isotopes are identical") { REQUIRE(isotope_2 == isotope_1); } } } AND_WHEN("A new isotope is copy-constructed") { Isotope isotope_3(isotope_1); THEN("All the member data are set accordingly") { REQUIRE(isotope_3.getName().toStdString() == "carbon"); REQUIRE(isotope_3.getSymbol().toStdString() == "C"); REQUIRE(isotope_3.getMass() == 12.0); REQUIRE(isotope_3.getProbability() == 0.989211941850466902614869013632414862513542175292968750000000); AND_THEN("The Isotope should both be valid and validate successfully") { REQUIRE(isotope_3.isValid()); QVector error_list; REQUIRE(isotope_3.validate(&error_list)); REQUIRE(error_list_p->size() == 0); } AND_THEN("Both isotopes are identical") { REQUIRE(isotope_3 == isotope_1); } } } AND_WHEN("A new isotope is assignment-operator-constructed") { Isotope isotope_4 = isotope_1; THEN("All the member data are set accordingly") { REQUIRE(isotope_4.getName().toStdString() == "carbon"); REQUIRE(isotope_4.getSymbol().toStdString() == "C"); REQUIRE(isotope_4.getMass() == 12.0); REQUIRE(isotope_4.getProbability() == 0.989211941850466902614869013632414862513542175292968750000000); AND_THEN("The Isotope should both be valid and validate successfully") { REQUIRE(isotope_4.isValid()); QVector error_list; REQUIRE(isotope_4.validate(&error_list)); REQUIRE(error_list_p->size() == 0); } AND_THEN("Both isotopes are identical") { REQUIRE(isotope_4 == isotope_1); } } } AND_WHEN( "An absurd isotope is constructed with a failing descriptive string") { QString isotope_string = " nothing, N, 600, 1.3"; Isotope isotope_5(isotope_string); THEN("The isotope has to be different than the other correct ones") { REQUIRE(isotope_5 != isotope_1); } } } } SCENARIO("Isotope construction with incorrect arguments", "[Isotope]") { TestUtils test_utils; QVector error_list; WHEN("Constructing Isotope with failing element's name") { Isotope isotope( "Carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } AND_WHEN("Copy-constructing a new Isotope using it") { Isotope isotope_new(isotope); THEN("It is also invalid") { REQUIRE_FALSE(isotope_new.isValid()); } } } WHEN("Constructing Isotope with all failing members") { Isotope isotope("Carbon", "c", -1, 1.1); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } AND_WHEN("Copy-constructing a new Isotope using it") { Isotope isotope_new(isotope); THEN("It is also invalid") { REQUIRE_FALSE(isotope_new.isValid()); } } } WHEN("Constructing Isotope with failing element's name") { Isotope isotope( "1Carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Constructing Isotope with failing element's symbol") { Isotope isotope( "carbon", "c", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Constructing Isotope with failing element's symbol") { Isotope isotope( "carbon", "1c", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Constructing Isotope with failing mass") { Isotope isotope( "carbon", "C", -0.00001, 0.989211941850466902614869013632414862513542175292968750000000); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Constructing Isotope with failing probability") { Isotope isotope("carbon", "C", 12.0, -0.00000001); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Constructing Isotope with failing probability") { Isotope isotope("carbon", "C", 12.0, 1.0000001); THEN("The isotope is invalid.") { REQUIRE_FALSE(isotope.isValid()); } } } SCENARIO("Isotope instances can be initialized with the four parameters", "[Isotope]") { TestUtils test_utils; QVector error_list; GIVEN("An empty Isotope") { Isotope isotope; WHEN("Initialized with all proper arguments") { isotope.initialize("carbon", "C", 12.0, 0.9892119418504669026); THEN("The Isotope should be valid") { REQUIRE(isotope.isValid()); REQUIRE(isotope.validate(&error_list)); } } WHEN("Intialized with failing name") { isotope.initialize("Carbon", "C", 12.0, 0.9892119418504669026); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing name") { isotope.initialize("", "C", 12.0, 0.9892119418504669026); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing symbol") { isotope.initialize("carbon", "c", 12.0, 0.9892119418504669026); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing symbol") { isotope.initialize("carbon", "", 12.0, 0.9892119418504669026); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing mass") { isotope.initialize("carbon", "C", -0.00000120, 0.9892119418504669026); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing probability") { isotope.initialize("carbon", "C", 12.0, -0.00001); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } WHEN("Initialized with failing probability") { isotope.initialize("carbon", "C", 12.0, 1.00001); THEN("The Isotope should not be valid") { REQUIRE_FALSE(isotope.isValid()); } } } } SCENARIO("Isotope instances can be initialized piecemeal with setter functions", "[Isotope]") { QVector error_list; GIVEN("An empty Isotope instance") { Isotope isotope; THEN("The instance is invalid") { REQUIRE_FALSE(isotope.isValid()); } WHEN("Failing name is set") { isotope.setName("carbon"); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing name is set") { isotope.setName(""); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing symbol is set") { isotope.setSymbol("carbon"); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing symbol is set") { isotope.setSymbol(""); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing mass is set") { isotope.setMass(-0.00001); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing probability is set") { isotope.setProbability(-0.00001); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Failing probability is set") { isotope.setProbability(1.000001); THEN("The Isotope is invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } } WHEN("Proper name is set") { isotope.setName("carbon"); THEN("The Isotope is still invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } AND_WHEN("Proper symbol is set") { isotope.setSymbol("C"); THEN("The Isotope is still invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } AND_WHEN("Proper mass is set") { isotope.setMass(12.00000); THEN("The Isotope is still invalid") { REQUIRE_FALSE(isotope.validate(&error_list)); } AND_WHEN("Proper probability is set") { isotope.setProbability(0.988); THEN("The Isotope is finally valid") { REQUIRE(isotope.validate(&error_list)); } } } } } } } SCENARIO("Isotope instances can be initialized with a descriptive text", "[Isotope]") { TestUtils test_utils; QVector error_list; // Easy means to craft various lengths of descriptive text strings. QStringList isotope_descriptive_strings; isotope_descriptive_strings << "carbon" << "C" << "12.000000000000000000000000000000000000000000000000000000000000" << "0.989211941850466902614869013632414862513542175292968750000000"; QString isotope_descriptive_string = isotope_descriptive_strings.join(","); GIVEN("Two correct and identical Isotope instances") { Isotope isotope_1( "carbon", "C", 12.000000000000000000000000000000000000000000000000000000000000, 0.989211941850466902614869013632414862513542175292968750000000); REQUIRE(isotope_1.isValid()); REQUIRE(isotope_1.validate(&error_list)); Isotope isotope_2( "carbon", "C", 12.000000000000000000000000000000000000000000000000000000000000, 0.989211941850466902614869013632414862513542175292968750000000); REQUIRE(isotope_2.isValid()); REQUIRE(isotope_2.validate(&error_list)); REQUIRE(isotope_1 == isotope_2); WHEN("Self-documenting one of them to a string") { QString isotope_1_string = isotope_1.toString(); THEN("The string has to be correct") { REQUIRE(isotope_1_string.toStdString() == isotope_descriptive_string.toStdString()); } AND_WHEN("The descriptive text is cleared") { isotope_descriptive_string.clear(); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(isotope_descriptive_string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The descriptive text is incomplete") { QStringList strings = isotope_descriptive_strings; strings.removeLast(); QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The element name in the descriptive text is incorrect") { QStringList strings = isotope_descriptive_strings; strings[0] = "1234"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The element name in the descriptive text starts uppercase") { QStringList strings = isotope_descriptive_strings; strings[0] = "Carbon"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The symbol in the descriptive text is incorrect") { QStringList strings = isotope_descriptive_strings; strings[1] = "1234"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The symbol in the descriptive text is lowercase") { QStringList strings = isotope_descriptive_strings; strings[1] = "c"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The mass in the descriptive text is < 0") { QStringList strings = isotope_descriptive_strings; strings[2] = "-0.0000001"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The mass in the descriptive text is not double") { QStringList strings = isotope_descriptive_strings; strings[2] = "waouw"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The probability in the descriptive text is < 0") { QStringList strings = isotope_descriptive_strings; strings[3] = "-0.0000001"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The probability in the descriptive text is > 1") { QStringList strings = isotope_descriptive_strings; strings[3] = "1.0000001"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } AND_WHEN("The probability in the descriptive text is not double") { QStringList strings = isotope_descriptive_strings; strings[3] = "a1254"; QString string = strings.join(","); THEN("Initialization of an Isotope instance using it should fail") { REQUIRE(isotope_1.initialize(string) == false); AND_THEN("The isotope should become invalid") { REQUIRE_FALSE(isotope_1.isValid()); } } } } } } SCENARIO("All Isotope member data can be set using setters", "[Isotope]") { TestUtils test_utils; QVector error_list; WHEN("An absurd Isotope is created starting for a failing descriptive string") { QString isotope_string = " 0, nothing, N, 600, 1200, 1200, 1,"; isotope_string += "1.3,"; isotope_string += "-2,"; isotope_string += "0 "; Isotope isotope_1(isotope_string); AND_WHEN("Using setters to configure it and make it a correct Isotope") { // Now test all the set functions. isotope_1.setName("carbon"); isotope_1.setSymbol("C"); isotope_1.setMass(12.0); isotope_1.setProbability( 0.989211941850466902614869013632414862513542175292968750000000); } AND_WHEN( "Another Isotope is constructed to be identical to the configured one") { Isotope isotope_2( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("Both isotopes need to turn out identical") { REQUIRE("isotope_1 == isotope_2"); } } } } SCENARIO("Isotope instances can be compared with operators ==() and !=()", "[Isotope]") { TestUtils test_utils; QVector error_list; WHEN("Two identical Isotope instances are created") { Isotope isotope_1( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); Isotope isotope_2( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("Operators ==() and !=() should return correct results") { REQUIRE(isotope_1 == isotope_2); REQUIRE_FALSE(isotope_1 != isotope_2); } AND_WHEN("One of the isotopes is modified") { isotope_1.setSymbol("N"); THEN("Operators ==() and !=() should return correct results") { REQUIRE(isotope_1 != isotope_2); REQUIRE_FALSE(isotope_1 == isotope_2); } } } } SCENARIO("Failed validation of Isotope instances report error strings", "[Isotope]") { TestUtils test_utils; QVector error_list; WHEN("A correct Isotope is instanciated") { Isotope isotope_1( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("Validation should be successful with no error strings") { REQUIRE(isotope_1.isValid()); REQUIRE(isotope_1.validate(&error_list)); REQUIRE(!error_list_p->size()); } AND_WHEN("The symbol is crippled") { isotope_1.setSymbol(""); THEN("Validation should return an informative string") { REQUIRE_FALSE(isotope_1.validate(&error_list)); REQUIRE(error_list_p->size() == 1); REQUIRE(error_list.front().toStdString() == "Failed to validate the element's symbol."); } } AND_WHEN("The mass is also crippled") { isotope_1.setSymbol(""); isotope_1.setMass(-10); THEN("Validation should return an informative string") { error_list.clear(); REQUIRE_FALSE(isotope_1.validate(&error_list)); REQUIRE(error_list_p->size() == 2); REQUIRE(error_list.front().toStdString() == "Failed to validate the element's symbol."); REQUIRE(error_list.back().toStdString() == "Failed to validate the isotope's mass."); } } AND_WHEN("The probability is also crippled") { isotope_1.setSymbol(""); isotope_1.setMass(-10); isotope_1.setProbability(-0.10); THEN("Validation should return an informative string") { error_list.clear(); REQUIRE_FALSE(isotope_1.validate(&error_list)); REQUIRE(error_list.front().toStdString() == "Failed to validate the element's symbol."); REQUIRE(error_list.back().toStdString() == "Failed to validate the isotope's probability."); } } } } SCENARIO("Isotope instances can be self-documenting with as descriptive string", "[Isotope]") { TestUtils test_utils; QVector error_list; WHEN("Instantiating a correct Isotope") { Isotope isotope_1( "carbon", "C", 12.0, 0.989211941850466902614869013632414862513542175292968750000000); THEN("That Isotope instance can produce a descriptive string") { QString isotope_string = isotope_1.toString(); REQUIRE(isotope_string.toStdString() == "carbon,C," "12.000000000000000000000000000000000000000000000000000000000000," "0.989211941850466902614869013632414862513542175292968750000000"); AND_WHEN("That descriptive string is used to instantiate a new Isotope") { Isotope isotope_2(isotope_string); THEN("Both isotopes need to be identical") { REQUIRE(isotope_1 == isotope_2); REQUIRE_FALSE(isotope_1 != isotope_2); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_IsotopicData.cpp000664 001750 001750 00000050103 15100504560 022724 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("Creation of an empty IsotopicData instance", "[IsotopicData]") { TestUtils test_utils; test_utils.initializeXpertmassLibrary(); WHEN("Creating an empty IsotopicData instance") { IsotopicData isotopic_data; THEN("It is created invalid") { REQUIRE_FALSE(isotopic_data.isValid()); } } } SCENARIO( "Adding Isotope instances to an IsotopicData instance without map updating", "[IsotopicData]") { TestUtils test_utils; QVector error_list; GIVEN("An empty IsotopicData instance and some Isotope instances") { IsotopicData isotopic_data; REQUIRE(test_utils.msp_isotopeC12->isValid()); REQUIRE(test_utils.msp_isotopeC12->validate(&error_list)); REQUIRE(test_utils.msp_isotopeC13->isValid()); REQUIRE(test_utils.msp_isotopeC13->validate(&error_list)); REQUIRE(test_utils.msp_isotopeN14->isValid()); REQUIRE(test_utils.msp_isotopeN14->validate(&error_list)); REQUIRE(test_utils.msp_isotopeN15->isValid()); REQUIRE(test_utils.msp_isotopeN15->validate(&error_list)); QList isotopes; // isotopes.push_back(test_utils.msp_isotopeC12); // isotopes.push_back(test_utils.msp_isotopeC13); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); THEN("All the containers are empty") { REQUIRE(isotopic_data.getIsotopes().size() == 0); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 0); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 0); REQUIRE(isotopic_data.updateMassMaps() == 0); } WHEN("Appending a new Isotope instance without updating the maps") { isotopic_data.appendNewIsotope(test_utils.msp_isotopeC12, /*update_maps*/ false); THEN("It has that new Isotope in the m_isotopes member") { REQUIRE(isotopic_data.size() == 1); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 1); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 1); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder() .front() .toStdString() == "C"); REQUIRE(isotopic_data.validate(&error_list)); } THEN("It cannot validate because there is only one isotope") { REQUIRE_FALSE(isotopic_data.validateBySymbol("C", error_list)); } THEN( "Getting masses is not possible because the maps have not been updated") { bool ok = false; REQUIRE(isotopic_data.getMonoMassBySymbol("C", ok) == 0); REQUIRE_FALSE(ok); REQUIRE(isotopic_data.getAvgMassBySymbol("C", ok) == 0); REQUIRE_FALSE(ok); } WHEN("Adding the second carbon isotope without updating the maps") { isotopic_data.appendNewIsotope(test_utils.msp_isotopeC13, /*update_maps*/ false); THEN("Still cannot get masses") { bool ok = false; REQUIRE(isotopic_data.getMonoMassBySymbol("C", ok) == 0); REQUIRE_FALSE(ok); REQUIRE(isotopic_data.getAvgMassBySymbol("C", ok) == 0); REQUIRE_FALSE(ok); } AND_WHEN("Updating the maps explicitely") { REQUIRE(isotopic_data.updateMassMaps() == 1); isotopic_data.updateMassMaps("C"); THEN("The masses finally become available") { bool ok = false; REQUIRE_THAT(isotopic_data.getMonoMassBySymbol("C", ok), Catch::Matchers::WithinAbs(12.00000, 0.0000000001)); REQUIRE_THAT( isotopic_data.getAvgMassBySymbol("C", ok), Catch::Matchers::WithinAbs(12.0108242503, 0.0000000001)); } THEN("Cumulated probabilities for isotopes C and N equal 1") { REQUIRE(isotopic_data.getCumulatedProbabilitiesBySymbol( "C", error_list) == 1); } } AND_WHEN("Adding the two isotopes of N as a vector with maps updating") { isotopic_data.appendNewIsotopes(isotopes, /*update maps*/ true); THEN("The full set of isotopes for C and N can be tested") { REQUIRE(isotopic_data.getIsotopeCountBySymbol("C") == 2); REQUIRE(isotopic_data.getIsotopeCountBySymbol("N") == 2); REQUIRE(isotopic_data.getCumulatedProbabilitiesBySymbol( "C", error_list) == 1); REQUIRE(isotopic_data.getCumulatedProbabilitiesBySymbol( "N", error_list) == 1); } } } } } } SCENARIO( "Adding Isotope instances to an IsotopicData instance with map updating", "[IsotopicData]") { TestUtils test_utils; QVector error_list; IsotopicData isotopic_data; REQUIRE(test_utils.msp_isotopeC12->isValid()); REQUIRE(test_utils.msp_isotopeC12->validate(&error_list)); REQUIRE(test_utils.msp_isotopeC13->isValid()); REQUIRE(test_utils.msp_isotopeC13->validate(&error_list)); REQUIRE(test_utils.msp_isotopeN14->isValid()); REQUIRE(test_utils.msp_isotopeN14->validate(&error_list)); REQUIRE(test_utils.msp_isotopeN15->isValid()); REQUIRE(test_utils.msp_isotopeN15->validate(&error_list)); QList isotopes; // isotopes.push_back(test_utils.msp_isotopeC12); isotopes.push_back(test_utils.msp_isotopeC13); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); GIVEN("An empty IsotopicData instance and some Isotope instances") { WHEN("Appending a new Isotope instance with maps update") { isotopic_data.appendNewIsotope(test_utils.msp_isotopeC12); THEN("It has that new Isotope in the m_isotopes member") { REQUIRE(isotopic_data.size() == 1); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 1); REQUIRE(isotopic_data.validate(&error_list)); } THEN("It cannot validate because there is only one isotope") { REQUIRE_FALSE(isotopic_data.validateBySymbol("C", error_list)); } THEN("Getting masses is possible because the maps have been updated") { bool ok = false; REQUIRE(isotopic_data.getMonoMassBySymbol("C", ok) == 12.0); REQUIRE(ok); REQUIRE(isotopic_data.getAvgMassBySymbol("C", ok) == 12.0); REQUIRE(ok); } AND_WHEN("Appending new Isotope instances in one go") { isotopic_data.appendNewIsotopes(isotopes, /*update_maps*/ true); THEN("All sorts of validations should be successful") { REQUIRE(isotopic_data.validate(&error_list)); REQUIRE(isotopic_data.validateBySymbol("C", error_list)); REQUIRE(isotopic_data.validateBySymbol("N", error_list)); int count = 0; REQUIRE(isotopic_data.containsSymbol("C", count)); REQUIRE(count == 2); REQUIRE(isotopic_data.containsSymbol("N", count)); REQUIRE(count == 2); REQUIRE(isotopic_data.containsName("carbon", count)); REQUIRE(count == 2); REQUIRE(isotopic_data.containsName("nitrogen", count)); REQUIRE(count == 2); REQUIRE(isotopic_data.getIsotopeCountBySymbol("C") == 2); REQUIRE(isotopic_data.getIsotopeCountBySymbol("N") == 2); REQUIRE(isotopic_data.getCumulatedProbabilitiesBySymbol( "C", error_list) == 1); REQUIRE(isotopic_data.getCumulatedProbabilitiesBySymbol( "N", error_list) == 1); } } } } isotopic_data.clear(); isotopes.clear(); GIVEN("An isotopic data instance that has been cleared.") { THEN("The various member containers should be back emtpy") { REQUIRE(isotopic_data.getIsotopes().size() == 0); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 0); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 0); REQUIRE(isotopic_data.updateMassMaps() == 0); } } GIVEN( "Addition of a set of three isotopes in a row (C12,*not C13*,N14,N15) with " "maps update") { isotopes.push_back(test_utils.msp_isotopeC12); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); isotopic_data.appendNewIsotopes(isotopes, /* update maps */ true); THEN("The various container members should contain multiple items.") { REQUIRE(isotopic_data.getIsotopes().size() == 3); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 2); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 2); REQUIRE(isotopic_data.updateMassMaps() == 2); } THEN( "Checking the index of the C12 isotope and inserting the missing C13 " "isotope") { IsotopeListCstIteratorPair iter_pair = isotopic_data.getIsotopesBySymbol("C"); std::size_t index = std::distance(std::begin(isotopic_data.getIsotopes()), iter_pair.first); REQUIRE(index == 0); // We insert C13 right after C12, so that they are in the right order: C12 // and C13. isotopic_data.insertNewIsotope( test_utils.msp_isotopeC13, index + 1, /*update maps*/ true); AND_THEN( "The two C isotopes should come in order C12 and C13 " "with proper masses and probabilities.") { iter_pair = isotopic_data.getIsotopesBySymbol("C"); // Check by the Isotope instances themselves. REQUIRE(iter_pair.first->get()->getMass() == 12.0); REQUIRE_THAT(std::prev(iter_pair.second)->get()->getMass(), Catch::Matchers::WithinAbs(13.0033548352, 0.0000000001)); // Check by the symbol-based iterator pair, which ensures that // the average mass calculation involved two immediately consecutive // isotopes. REQUIRE_THAT(isotopic_data.getMonoMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0, 0.0000000001)); REQUIRE_THAT(isotopic_data.computeAvgMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0108242503, 0.0000000001)); REQUIRE( isotopic_data.getCumulatedProbabilities(iter_pair, error_list) == 1); REQUIRE_THAT( iter_pair.first->get()->getProbability(), Catch::Matchers::WithinAbs( 0.989211941850466902614869013632414862513542175292968750000000, 0.0000000001)); REQUIRE_THAT( std::prev(iter_pair.second)->get()->getProbability(), Catch::Matchers::WithinAbs( 0.010788058149533083507343178553128382191061973571777343750000, 0.0000000001)); bool ok = false; REQUIRE_THAT(isotopic_data.getAvgMassBySymbol("C", ok), Catch::Matchers::WithinAbs(12.0108242503, 0.0000000001)); } AND_THEN( "Let's check that the average mass is correct also for the N symbol") { bool ok = false; iter_pair = isotopic_data.getIsotopesBySymbol("N"); // Check by the Isotope instances themselves. REQUIRE(iter_pair.first->get()->getMass() == 14.0030740042); REQUIRE_THAT(std::prev(iter_pair.second)->get()->getMass(), Catch::Matchers::WithinAbs(15.0001088994, 0.0000000001)); // Check by the symbol-based iterator pair, which ensures that // the average mass calculation involved two immediately consecutive // isotopes. REQUIRE_THAT(isotopic_data.getMonoMass(iter_pair, error_list), Catch::Matchers::WithinAbs(14.0030740042, 0.0000000001)); REQUIRE_THAT(isotopic_data.computeAvgMass(iter_pair, error_list), Catch::Matchers::WithinAbs(14.0067051908, 0.0000000001)); REQUIRE_THAT(isotopic_data.getAvgMassBySymbol("N", ok), Catch::Matchers::WithinAbs(14.0067051908, 0.0000000001)); REQUIRE( isotopic_data.getCumulatedProbabilities(iter_pair, error_list) == 1); } } } } SCENARIO("Accessing Isotope instances using iterators", "[IsotopicData]") { TestUtils test_utils; QList isotopes; isotopes.push_back(test_utils.msp_isotopeC12); isotopes.push_back(test_utils.msp_isotopeC13); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); IsotopicData isotopic_data; isotopic_data.appendNewIsotopes(isotopes, /*update_maps*/ true); QVector error_list; GIVEN("An IsotopicData instance with the four C and N isotopes") { isotopic_data.appendNewIsotopes(isotopes, /*update_maps*/ true); WHEN("Asking for isotopes of 'carbon' via iterators") { IsotopeListCstIteratorPair iter_pair = isotopic_data.getIsotopesByName("carbon"); THEN("The masses might be asked") { REQUIRE_THAT(isotopic_data.getMonoMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0, 0.0000000001)); REQUIRE_THAT(isotopic_data.computeAvgMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0108242503, 0.0000000001)); REQUIRE_THAT( isotopic_data.getCumulatedProbabilities(iter_pair, error_list), Catch::Matchers::WithinAbs(1, 0.0000000001)); } } WHEN("Asking for isotopes of 'C' via iterators") { IsotopeListCstIteratorPair iter_pair = isotopic_data.getIsotopesBySymbol("C"); THEN("The masses might be asked") { REQUIRE_THAT(isotopic_data.getMonoMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0, 0.0000000001)); REQUIRE_THAT(isotopic_data.computeAvgMass(iter_pair, error_list), Catch::Matchers::WithinAbs(12.0108242503, 0.0000000001)); REQUIRE_THAT( isotopic_data.getCumulatedProbabilities(iter_pair, error_list), Catch::Matchers::WithinAbs(1, 0.0000000001)); } } } } SCENARIO("Isotopes can be removed from IsotopicData", "[IsotopicData]") { TestUtils test_utils; QList isotopes; isotopes.push_back(test_utils.msp_isotopeC12); isotopes.push_back(test_utils.msp_isotopeC13); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); IsotopicData isotopic_data; bool ok; QVector error_list; WHEN("There are no isotopes in the isotopic data, no isotope can be removed") { QList::const_iterator iterator; iterator = isotopic_data.eraseIsotopes(0, 20, false); REQUIRE(iterator == isotopic_data.getIsotopes().cend()); } WHEN("The isotopic data are filled with the four (C12,C13,N14,N15) isotopes") { isotopic_data.appendNewIsotopes(isotopes, /* update maps */ true); THEN("The sizes of the containers can be checked") { REQUIRE(isotopic_data.getIsotopes().size() == 4); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 2); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 2); REQUIRE(isotopic_data.updateMassMaps() == 2); } AND_THEN("Erasing an isotope out-of-bound fails") { QList::const_iterator iterator; iterator = isotopic_data.eraseIsotopes(4, 20, false); REQUIRE(iterator == isotopic_data.getIsotopes().cend()); } AND_THEN("If the two C isotopes are erased, the container sizes change") { isotopic_data.eraseIsotopes(0, 1, /*update maps*/ true); // qDebug() << "The new size: " << isotopic_data.getIsotopes().size(); REQUIRE(isotopic_data.getIsotopes().size() == 2); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 1); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 1); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().at(0) == "N"); REQUIRE(isotopic_data.updateMassMaps() == 1); } AND_THEN("Check the masses for the remaining N isotopes") { IsotopeListCstIteratorPair iter_pair = isotopic_data.getIsotopesBySymbol("N"); REQUIRE(std::distance(iter_pair.first, iter_pair.second) == 2); // Check by the Isotope instances themselves. REQUIRE(iter_pair.first->get()->getMass() == 14.0030740042); REQUIRE_THAT(std::prev(iter_pair.second)->get()->getMass(), Catch::Matchers::WithinAbs(15.0001088994, 0.0000000001)); // Check by the symbol-based iterator pair, which ensures that // the average mass calculation involved two immediately consecutive // isotopes. REQUIRE_THAT(isotopic_data.getMonoMass(iter_pair, error_list), Catch::Matchers::WithinAbs(14.0030740042, 0.0000000001)); REQUIRE_THAT(isotopic_data.computeAvgMass(iter_pair, error_list), Catch::Matchers::WithinAbs(14.0067051908, 0.0000000001)); REQUIRE_THAT(isotopic_data.getAvgMassBySymbol("N", ok), Catch::Matchers::WithinAbs(14.0067051908, 0.0000000001)); REQUIRE(isotopic_data.getCumulatedProbabilities(iter_pair, error_list) == 1); } } } SCENARIO("IsotopicData can be copied, replaced and compared", "[IsotopicData]") { TestUtils test_utils; QList isotopes; isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); IsotopicData isotopic_data; WHEN("The isotopic data are filled with the two (N14,N15) isotopes") { isotopic_data.appendNewIsotopes(isotopes); THEN("The sizes of the containers can be checked") { REQUIRE(isotopic_data.getIsotopes().size() == 2); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 1); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 1); REQUIRE(isotopic_data.updateMassMaps() == 1); } AND_THEN("We test the copy constructor") { IsotopicData isotopic_data_1(isotopic_data); REQUIRE(isotopic_data_1 == isotopic_data); } AND_THEN("We test the assignement operator") { IsotopicData isotopic_data_1(isotopic_data); REQUIRE(isotopic_data_1 == isotopic_data); } AND_THEN("We replace one isotope by another and check") { isotopic_data.replace(test_utils.msp_isotopeC12, test_utils.msp_isotopeN14); REQUIRE(isotopic_data.getIsotopes().at(0)->getName().toStdString() == "nitrogen"); } } } SCENARIO("IsotopicData can be validated in various ways", "[IsotopicData]") { TestUtils test_utils; IsotopicData isotopic_data; QVector error_list; QList isotopes; isotopes.push_back(test_utils.msp_isotopeC12); isotopes.push_back(test_utils.msp_isotopeC13); isotopes.push_back(test_utils.msp_isotopeN14); isotopes.push_back(test_utils.msp_isotopeN15); isotopic_data.appendNewIsotopes(isotopes, /* update maps */ true); WHEN("The isotopic data are filled with the four (C12,C13,N14,N15) isotopes") { THEN("The sizes of the containers can be checked") { REQUIRE(isotopic_data.getIsotopes().size() == 4); REQUIRE(isotopic_data.getUniqueSymbolsCount() == 2); REQUIRE(isotopic_data.getUniqueSymbolsInOriginalOrder().size() == 2); REQUIRE(isotopic_data.updateMassMaps() == 2); } AND_THEN("The isotopic data can be validated generically") { bool result = isotopic_data.validate(&error_list); REQUIRE(result); REQUIRE(error_list_p->size() == 0); } AND_THEN( "The isotopic data can be validated by symbol (more powerful checks)") { bool result = isotopic_data.validateBySymbol("C", error_list); REQUIRE(result); REQUIRE(error_list_p->size() == 0); result = isotopic_data.validateBySymbol("N", error_list); REQUIRE(result); REQUIRE(error_list_p->size() == 0); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_IsotopicDataLibraryHandler.cpp000664 001750 001750 00000010027 15100504560 025550 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("IsotopicDataLibraryHandler loads data from IsoSpec++'s tables", "[IsotopicDataLibraryHandler]") { TestUtils test_utils; test_utils.initializeXpertmassLibrary(); IsotopicDataLibraryHandler isotopic_data_lib_handler; WHEN("Constructed, the data are empty") { THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_lib_handler.getIsotopicData() != nullptr); } AND_WHEN("The file name is set by the setter (although not used here)") { isotopic_data_lib_handler.setFileName("the_file_name.dat"); THEN("The getter returns the proper file name (although not used here)") { REQUIRE(isotopic_data_lib_handler.getFileName().toStdString() == "the_file_name.dat"); } } } AND_WHEN("Loading isotopic data from library") { std::size_t count_non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(count_non_isotope_skipped_items); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count + count_non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_lib_handler.getIsotopicData()->size() == loaded_isotope_count); } } AND_WHEN("A new IsotopicDataLibraryHandler instance is copy constructed") { long use_count_before_copy = isotopic_data_lib_handler.getIsotopicData().use_count(); IsotopicDataLibraryHandler isotopic_data_lib_handler_1( isotopic_data_lib_handler); THEN("The instances are checked and should be identical") { REQUIRE(isotopic_data_lib_handler.getIsotopicData()->size() == isotopic_data_lib_handler_1.getIsotopicData()->size()); long use_count_after_copy = isotopic_data_lib_handler.getIsotopicData().use_count(); REQUIRE(use_count_after_copy == use_count_before_copy + 1); } } } SCENARIO("IsotopicDataLibraryHandler writes data to file", "[IsotopicDataLibraryHandler]") { IsotopicDataLibraryHandler isotopic_data_lib_handler; WHEN("Data have been loaded, the data are checked") { std::size_t non_isotope_skipped_items = 0; std::size_t loaded_isotope_count = isotopic_data_lib_handler.loadData(non_isotope_skipped_items); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count + non_isotope_skipped_items == IsoSpec::isospec_number_of_isotopic_entries); REQUIRE(isotopic_data_lib_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Isotopic data are written to file") { QString isotopes_output_dir = QString("%1/%2").arg(TESTS_OUTPUT_DIR).arg("isotopes"); QDir dir; bool result = dir.mkpath(isotopes_output_dir); REQUIRE(result == true); QString output_file_name = QString("%1/%2").arg(isotopes_output_dir).arg("isospec-tables.dat"); // qDebug() << "The write file name: " << output_file_name.toStdString() // << std::endl; std::size_t written_isotope_count = isotopic_data_lib_handler.writeData(output_file_name); THEN("The count of written isotopes is checked") { REQUIRE(written_isotope_count == loaded_isotope_count); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_IsotopicDataManualConfigHandler.cpp000664 001750 001750 00000025361 15100504560 026516 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("Construction of an empty IsotopicDataManualConfigHandler", "[IsotopicDataManualConfigHandler]") { TestUtils test_utils; test_utils.initializeXpertmassLibrary(); QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR) .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName); GIVEN("An entirely empty IsotopicDataManualConfigHandler instance") { IsotopicDataManualConfigHandler isotopic_data_manual_config_handler; WHEN("Constructed like so") { THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == 0); REQUIRE( isotopic_data_manual_config_handler.getFileName().toStdString() == ""); } AND_WHEN("The file name is set using the setter") { isotopic_data_manual_config_handler.setFileName( isotopic_data_file_path); THEN("The file name is set") { REQUIRE( isotopic_data_manual_config_handler.getFileName().toStdString() == isotopic_data_file_path.toStdString()); } } } } GIVEN( "An IsotopicDataManualConfigHandler instance constructed with aan empty " "file name") { IsotopicDataManualConfigHandler isotopic_data_manual_config_handler(""); WHEN("Constructed like so") { THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == 0); REQUIRE( isotopic_data_manual_config_handler.getFileName().toStdString() == ""); } AND_WHEN("The file name is set using the setter") { isotopic_data_manual_config_handler.setFileName( isotopic_data_file_path); THEN("The file name is set") { REQUIRE( isotopic_data_manual_config_handler.getFileName().toStdString() == isotopic_data_file_path.toStdString()); } } } } } SCENARIO( "Construction of an IsotopicDataManualConfigHandler with an inexistent " "file's " "name", "[IsotopicDataManualConfigHandler]") { TestUtils test_utils; QString inexistent_isotopic_data_file_path = QString("%1/%2/%3").arg(TESTS_INPUT_DIR, "isotopes", "inexistent.dat"); GIVEN( "A IsotopicDataManualConfigHandler instance constructed with an inexistent " "file's name") { IsotopicDataManualConfigHandler isotopic_data_manual_config_handler( inexistent_isotopic_data_file_path); THEN("All the data are set to nothing, but the IsotopicData") { REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == 0); REQUIRE(isotopic_data_manual_config_handler.getFileName().toStdString() == inexistent_isotopic_data_file_path.toStdString()); } WHEN("Trying to load the data") { std::size_t loaded_isotope_count = isotopic_data_manual_config_handler.loadData(); THEN("The data cannot be loaded") { REQUIRE(loaded_isotope_count == 0); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Trying to load the data specifying an existing file's name") { QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR) .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName); isotopic_data_manual_config_handler.setFileName( isotopic_data_file_path); std::size_t loaded_isotope_count = isotopic_data_manual_config_handler.loadData(); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE( isotopic_data_manual_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } } } GIVEN( "A IsotopicDataManualConfigHandler instance constructed with an inexistent " "file's name") { IsotopicDataManualConfigHandler isotopic_data_manual_config_handler( inexistent_isotopic_data_file_path); THEN("All the data are set to nothing, but the IsotopicData") { REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == 0); REQUIRE(isotopic_data_manual_config_handler.getFileName().toStdString() == inexistent_isotopic_data_file_path.toStdString()); } WHEN("Trying to load the data") { std::size_t loaded_isotope_count = isotopic_data_manual_config_handler.loadData(); THEN("The data cannot be loaded") { REQUIRE(loaded_isotope_count == 0); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Setting an existing file's name") { QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR) .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName); isotopic_data_manual_config_handler.setFileName( isotopic_data_file_path); AND_WHEN("Trying to load data") { std::size_t loaded_isotope_count = isotopic_data_manual_config_handler.loadData(); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE( isotopic_data_manual_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } } } } } SCENARIO( "Construction of an IsotopicDataManualConfigHandler with an existing file's " "name", "[IsotopicDataManualConfigHandler]") { TestUtils test_utils; QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR) .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName); WHEN("Constructed, the data are empty") { IsotopicDataManualConfigHandler isotopic_data_manual_config_handler( isotopic_data_file_path); THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_manual_config_handler.getIsotopicData() != nullptr); } AND_WHEN("Loading isotopic data") { std::size_t loaded_isotope_count = isotopic_data_manual_config_handler.loadData(); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(isotopic_data_manual_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } } } SCENARIO("IsotopicDataManualConfigHandler writes data to file", "[IsotopicDataManualConfigHandler]") { TestUtils test_utils; QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR) .arg("isotopes", test_utils.m_manualUserIsotopicDataFileName); QDir dir; QString isotopes_output_dir = QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes"); QString isotopic_data_output_file_path = QString("%1/%2").arg( isotopes_output_dir, test_utils.m_manualUserIsotopicDataFileName); bool result = dir.mkpath(isotopes_output_dir); assert(result); IsotopicDataManualConfigHandler iso_data_user_config_handler( isotopic_data_file_path); WHEN("Data have been loaded, the data are checked") { std::size_t loaded_isotope_count = iso_data_user_config_handler.loadData(isotopic_data_file_path); THEN("The number of isotopes loaded is checked") { REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Isotopic data are written to file") { std::size_t written_isotope_count = iso_data_user_config_handler.writeData(isotopic_data_output_file_path); THEN("The count of written isotopes is checked") { REQUIRE(written_isotope_count == loaded_isotope_count); } } } } SCENARIO( "IsotopicDataManualConfigHandler loads data from file previously " "written by writeData() test") { TestUtils test_utils; QDir dir; QString isotopes_output_dir = QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes"); QString isotopic_data_output_file_path = QString("%1/%2").arg( isotopes_output_dir, test_utils.m_manualUserIsotopicDataFileName); IsotopicDataManualConfigHandler iso_data_user_config_handler( isotopic_data_output_file_path); WHEN("Constructed, the data are empty") { THEN("The isotopic data are empty but they are allocated") { REQUIRE(iso_data_user_config_handler.getIsotopicData() != nullptr); } } AND_WHEN("Loading isotopic data from the file") { std::size_t loaded_isotope_count = iso_data_user_config_handler.loadData(isotopic_data_output_file_path); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } AND_WHEN("A new IsotopicDataManualConfigHandler instance is copy constructed") { long use_count_before_copy = iso_data_user_config_handler.getIsotopicData().use_count(); IsotopicDataManualConfigHandler iso_data_user_config_handler_1( iso_data_user_config_handler); THEN("The instances are checked and should be identical") { REQUIRE(iso_data_user_config_handler.getIsotopicData()->size() == iso_data_user_config_handler_1.getIsotopicData()->size()); long use_count_after_copy = iso_data_user_config_handler.getIsotopicData().use_count(); REQUIRE(use_count_after_copy == use_count_before_copy + 1); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_IsotopicDataUserConfigHandler.cpp000664 001750 001750 00000020332 15100504560 026210 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include #include #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("Construction of an empty IsotopicDataUserConfigHandler", "[IsotopicDataUserConfigHandler]") { TestUtils test_utils; test_utils.initializeXpertmassLibrary(); QString isotopic_data_file_path = QString("%1/%2/%3") .arg( TESTS_INPUT_DIR, "isotopes", test_utils.m_naturalIsotopicDataFileName); GIVEN("An entirely empty IsotopicDataUserConfigHandler instance") { IsotopicDataUserConfigHandler isotopic_data_user_config_handler; WHEN("Constructed like so") { THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_user_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == 0); REQUIRE(isotopic_data_user_config_handler.getFileName().toStdString() == ""); } AND_WHEN("The file name is set using the setter") { isotopic_data_user_config_handler.setFileName(isotopic_data_file_path); THEN("The file name is set") { REQUIRE( isotopic_data_user_config_handler.getFileName().toStdString() == isotopic_data_file_path.toStdString()); } } } } } SCENARIO( "Construction of an IsotopicDataUserConfigHandler with an inexistent file's " "name", "[IsotopicDataUserConfigHandler]") { TestUtils test_utils; QString inexistent_isotopic_data_file_path = QString("%1/%2/%3").arg(TESTS_INPUT_DIR, "isotopes", "inexistent.dat"); GIVEN( "A IsotopicDataUserConfigHandler instance constructed with an inexistent " "file's name") { IsotopicDataUserConfigHandler isotopic_data_user_config_handler( inexistent_isotopic_data_file_path); THEN("All the data are set to nothing, but the IsotopicData") { REQUIRE(isotopic_data_user_config_handler.getIsotopicData() != nullptr); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == 0); REQUIRE(isotopic_data_user_config_handler.getFileName().toStdString() == inexistent_isotopic_data_file_path.toStdString()); } WHEN("Trying to load the data") { std::size_t loaded_isotope_count = isotopic_data_user_config_handler.loadData(); THEN("The data cannot be loaded") { REQUIRE(loaded_isotope_count == 0); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Trying to load the data specifying an existing file's name") { QString isotopic_data_file_path = QString("%1/%2/%3") .arg(TESTS_INPUT_DIR, "isotopes", test_utils.m_naturalIsotopicDataFileName); isotopic_data_user_config_handler.setFileName(isotopic_data_file_path); std::size_t loaded_isotope_count = isotopic_data_user_config_handler.loadData(); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } } } } SCENARIO( "Construction of an IsotopicDataUserConfigHandler with an existing file's " "name", "[IsotopicDataUserConfigHandler]") { TestUtils test_utils; QString isotopic_data_file_path = QString("%1/%2/%3") .arg( TESTS_INPUT_DIR, "isotopes", test_utils.m_naturalIsotopicDataFileName); WHEN("Constructed, the data are empty") { IsotopicDataUserConfigHandler isotopic_data_user_config_handler( isotopic_data_file_path); THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_user_config_handler.getIsotopicData() != nullptr); } AND_WHEN("Loading isotopic data") { std::size_t loaded_isotope_count = isotopic_data_user_config_handler.loadData(); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } } } } SCENARIO( "IsotopicDataUserConfigHandler loads data from file previously " "written by writeDate test for IsotopicDataLibraryHandler") { QString isotopic_data_file_path = QString("%1/%2/%3").arg(TESTS_OUTPUT_DIR, "isotopes", "isospec-tables.dat"); WHEN("Constructed with a correct file name") { IsotopicDataUserConfigHandler isotopic_data_user_config_handler( isotopic_data_file_path); THEN("The isotopic data are empty but they are allocated") { REQUIRE(isotopic_data_user_config_handler.getIsotopicData() != nullptr); } AND_WHEN("Loading isotopic data from a user config file") { std::size_t loaded_isotope_count = isotopic_data_user_config_handler.loadData(isotopic_data_file_path); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN( "A new IsotopicDataUserConfigHandler instance is copy constructed") { long use_count_before_copy = isotopic_data_user_config_handler.getIsotopicData().use_count(); IsotopicDataUserConfigHandler isotopic_data_user_config_handler_1( isotopic_data_user_config_handler); THEN("The instances are checked and should be identical") { REQUIRE( isotopic_data_user_config_handler.getIsotopicData()->size() == isotopic_data_user_config_handler_1.getIsotopicData()->size()); long use_count_after_copy = isotopic_data_user_config_handler.getIsotopicData().use_count(); REQUIRE(use_count_after_copy == use_count_before_copy + 1); } } } } } SCENARIO("IsotopicDataUserConfigHandler writes data to file", "[IsotopicDataUserConfigHandler]") { TestUtils test_utils; QString isotopic_data_file_path = QString("%1/%2/%3") .arg( TESTS_INPUT_DIR, "isotopes", test_utils.m_naturalIsotopicDataFileName); WHEN("Constructed with a correct file name") { IsotopicDataUserConfigHandler isotopic_data_user_config_handler( isotopic_data_file_path); WHEN("Data have been loaded") { std::size_t loaded_isotope_count = isotopic_data_user_config_handler.loadData(isotopic_data_file_path); THEN("The number of isotopes loaded is checked") { REQUIRE(loaded_isotope_count > 0); REQUIRE(isotopic_data_user_config_handler.getIsotopicData()->size() == loaded_isotope_count); } AND_WHEN("Isotopic data are written to file") { QString isotopes_output_dir = QString("%1/%2").arg(TESTS_OUTPUT_DIR, "isotopes"); QDir dir; bool result = dir.mkpath(isotopes_output_dir); REQUIRE(result == true); QString output_file_name = QString("%1/%2").arg( isotopes_output_dir, "output_isotopic_data_user_config.dat"); // qDebug() << "The write file name: " << output_file_name; std::size_t written_isotope_count = isotopic_data_user_config_handler.writeData(output_file_name); THEN("The count of written isotopes is checked") { REQUIRE(written_isotope_count == loaded_isotope_count); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_MassCollection.cpp000664 001750 001750 00000013153 15100504560 023264 0ustar00rusconirusconi000000 000000 /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("MassCollection objects can be constructed with a name and masses", "[MassCollection]") { GIVEN( "A masses-as-text string and a vector of double values representing " "exactly the same data") { QString masses_as_text("123.321\n1234.4321\n12345.54321\n123456.654321"); std::vector masses{123.321, 1234.4321, 12345.54321, 123456.654321}; WHEN("MassCollection instances are created with these") { // Trick to test setName() MassCollection mass_collection_1("other_mono_masses", masses_as_text); mass_collection_1.setName("mono_masses"); mass_collection_1.setComment("This is a comment"); MassCollection mass_collection_2("mono_masses", masses); mass_collection_2.setComment("This is a comment"); THEN("Both MassCollection instances should be identical") { REQUIRE(mass_collection_1.isValid()); REQUIRE(mass_collection_2.isValid()); REQUIRE(mass_collection_1.getName().toStdString() == "mono_masses"); REQUIRE(mass_collection_1.getComment().toStdString() == "This is a comment"); REQUIRE(mass_collection_2.getName().toStdString() == "mono_masses"); REQUIRE(mass_collection_2.getComment().toStdString() == "This is a comment"); REQUIRE(mass_collection_1.size() == mass_collection_2.size()); for(std::size_t iter = 0; iter < mass_collection_1.size(); ++iter) REQUIRE(mass_collection_1.getMassesCstRef().at(iter) == mass_collection_2.getMassesCstRef().at(iter)); } WHEN("Copy-constructing or assigning new MassCollection objects") { MassCollection another_other_mass_collection(mass_collection_1); MassCollection other_mass_collection("different_name"); other_mass_collection = another_other_mass_collection; THEN("All the MassCollection instances should be identical") { REQUIRE(other_mass_collection.isValid()); REQUIRE(mass_collection_1.getName().toStdString() == "mono_masses"); REQUIRE(mass_collection_1.getComment().toStdString() == "This is a comment"); REQUIRE(other_mass_collection.size() == mass_collection_1.size()); for(std::size_t iter = 0; iter < mass_collection_1.size(); ++iter) { // Trick to use both versio of getMassesRef: REQUIRE(other_mass_collection.getMassesCstRef().at(iter) == mass_collection_1.getMassesRef().at(iter)); // Use also the other function getMassAtIndex() REQUIRE(other_mass_collection.getMassAtIndex(iter) == mass_collection_1.getMassAtIndex(iter)); } } } WHEN("Requesting the masses in the form of a text string") { QString masses_as_text = mass_collection_1.massesToText(); THEN("The text should be as expected") { REQUIRE(masses_as_text.toStdString() == "123.32100\n1234.43210\n12345.54321\n123456.65432\n"); } } WHEN("When adding one mass to the masses") { mass_collection_1.addMass(1.0000000000); QString masses_as_text = mass_collection_1.massesToText(); THEN("The new text should be as expected") { REQUIRE(masses_as_text.toStdString() == "124.32100\n1235.43210\n12346.54321\n123457.65432\n"); } } WHEN("When asking for MassCollection validation") { ErrorList error_list; bool is_valid = mass_collection_1.validate(&error_list); THEN("The MassCollection instance should validate succesfully") { REQUIRE(is_valid); } } WHEN("Sorting masses descending") { mass_collection_1.sortDescending(); THEN("The values should sort correctly") { REQUIRE_THAT(mass_collection_1.getMassesCstRef().front(), Catch::Matchers::WithinAbs(123456.654321, 0.0000000001)); REQUIRE_THAT(mass_collection_1.getMassesCstRef().back(), Catch::Matchers::WithinAbs(123.321, 0.0000000001)); } AND_WHEN("Sorted back in ascending order") { mass_collection_1.sortAscending(); THEN("The values should sort correctly") { REQUIRE_THAT(mass_collection_1.getMassesCstRef().front(), Catch::Matchers::WithinAbs(123.321, 0.0000000001)); REQUIRE_THAT( mass_collection_1.getMassesCstRef().back(), Catch::Matchers::WithinAbs(123456.654321, 0.0000000001)); } AND_WHEN("removeLessThan() is called") { mass_collection_1.removeLessThan(500); THEN("The remainig values should be correct") { REQUIRE_THAT(mass_collection_1.getMassesCstRef().front(), Catch::Matchers::WithinAbs(1234.4321, 0.0000000001)); REQUIRE_THAT( mass_collection_1.getMassesCstRef().back(), Catch::Matchers::WithinAbs(123456.654321, 0.0000000001)); } } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Modif.cpp000664 001750 001750 00000074301 15100504560 021405 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_modif("protein-1-letter", 1); ErrorList error_list; bool ok; int mdf_element_index = 0; int name_element_index = 1; int name_text_index = 2; int formula_element_index = 3; int formula_text_index = 4; int targets_element_index = 5; int targets_text_index = 6; int maxcount_element_index = 7; int maxcount_text_index = 8; QStringList dom_strings{"mdf", "name", "Acetylation", "formula", "-H2O+CH3COOH", "targets", "K", "maxcount", "1"}; SCENARIO("Construction of a Modif starting from empty", "[Modif]") { WHEN("A Modif is created empty") { Modif modif; THEN("It is invalid and does not validate successfully") { REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } qDebug() << "HERE"; THEN( "It is not possible to assess if it is known to the polymer chemistry " "definition") { REQUIRE(modif.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE); } AND_WHEN("Setting the PolChemDef") { modif.setPolChemDefCstSPtr(test_utils_1_letter_modif.msp_polChemDef); THEN( "It must be set but the Modif is still invalid and does not validate " "successfully") { REQUIRE(modif.getPolChemDefCstSPtr() == test_utils_1_letter_modif.msp_polChemDef); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } AND_WHEN("Setting an empty name") { modif.setName(""); THEN( "It must be set but the Modif is still invalid and does not validate " "successfully") { REQUIRE(modif.getName().toStdString() == ""); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } } AND_WHEN("Setting the name") { REQUIRE(modif.getPolChemDefCstSPtr() == test_utils_1_letter_modif.msp_polChemDef); modif.setName("Acetylation"); THEN( "It must be set but the Modif is still invalid and does not validate " "successfully") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } AND_WHEN("Setting a bad formula with title") { modif.setFormula("\"The title\"-h2O+cH3COOH"); THEN( "It must be set and the Modif is still invalid and does not " "validate " "successfully") { REQUIRE(modif.getFormula().toStdString() == "\"The title\"-h2O+cH3COOH"); REQUIRE(modif.formula(/*with_title*/ false).toStdString() == "-h2O+cH3COOH"); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } } AND_WHEN("Setting a valid formula with title") { REQUIRE(modif.getPolChemDefCstSPtr() == test_utils_1_letter_modif.msp_polChemDef); modif.setFormula("\"The title\"-H2O+CH3COOH"); THEN( "It must be set and now the Modif is not valid (targets not " "defined) and does not validate successfully") { qDebug() << "The modif:" << modif.toString(); REQUIRE(modif.getFormula().toStdString() == "\"The title\"-H2O+CH3COOH"); REQUIRE(modif.formula(/*with_title*/ false).toStdString() == "-H2O+CH3COOH"); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } AND_WHEN("Setting an erroneous targets string") { QString failing_targets_string("ST*Y!"); modif.setTargets(failing_targets_string); THEN("The Modif is not valid and does not validate successfully") { qDebug() << "The modif:" << modif.toString(); modif.validateTargets(/*simplify*/ true, ok); REQUIRE_FALSE(ok); modif.validateTargets( failing_targets_string, /*simplify*/ true, ok); REQUIRE_FALSE(ok); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } } AND_WHEN("Setting a correct targets string") { QString targets_string("K;R;M;C"); modif.setTargets(targets_string); THEN( "The Modif is not valid (max count not defined) and does not " "validate successfully") { qDebug() << "The modif:" << modif.toString(); modif.validateTargets(/*simplify*/ true, ok); REQUIRE(ok); modif.validateTargets(targets_string, /*simplify*/ true, ok); REQUIRE(ok); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } AND_WHEN("Setting an erroneous max count value") { modif.setMaxCount(0); THEN( "The Modif is not valid anymore and does not validate " "successfully") { qDebug() << "The modif:" << modif.toString(); REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } AND_WHEN("Setting a correct max count value") { modif.setMaxCount(2); THEN( "The Modif is valid and does validate " "successfully") { qDebug() << "The modif:" << modif.toString(); REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); } } } } } } } } } SCENARIO( "Construction of a Modif starting from valid PolChemDef, name and formula", "[Modif]") { WHEN("A Modif is created with PolChemDef, name and formula") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); THEN( "The Modif is not valid and does not validate successfully (targets and " "max count not defined)") { error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); REQUIRE_FALSE(modif.isValid()); AND_THEN("Calculating the masses should work nonetheless") { modif.calculateMasses(nullptr); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } } AND_WHEN("Setting proper targets and max count") { modif.setTargets("M;C"); modif.setMaxCount(3); THEN("It is valid and does validate successfully") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); } } } } SCENARIO("Setting bad values with setters invalidates a valid Modif", "[Modif]") { GIVEN( "A Modif created with PolChemDef, name and formula, and targets and max " "count set") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("K;R"); modif.setMaxCount(2); qDebug() << "At this point, the modif:" << modif.toString(); THEN( "The Modif is valid and validates successfully with masses properly " "calculated") { qDebug() << "BEFORE The modif: " << modif.toString(); REQUIRE(modif.isValid()); qDebug() << "AFTER The modif: " << modif.toString(); error_list.clear(); REQUIRE(modif.validate(&error_list)); modif.calculateMasses(nullptr); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } WHEN("Setting an empty name") { modif.setName(""); THEN("It is no more valid and does not validate successfully") { REQUIRE_FALSE(modif.isValid()); error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); } modif.setName("Acetylation"); } } } SCENARIO("A Modif can be searched for in the PolChemDef", "[Modif]") { WHEN("A Modif is created with PolChemDef, existing name and formula") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); THEN( "The Modif is not valid and does not validate successfully (targets and " "max count not defined) althought the masses are properly calculated") { error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); REQUIRE_FALSE(modif.isValid()); modif.calculateMasses(nullptr); double mono = 0; double avg = 0; REQUIRE(modif.accountMasses(mono, avg, /*times*/ 1) == modif); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); mono = 0; avg = 0; REQUIRE(modif.accountMasses(&mono, &avg, /*times*/ 1) == modif); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); mono = 0; avg = 0; REQUIRE(modif.accountMasses(nullptr, nullptr, /*times*/ 1) == modif); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(0, 0.0000000001)); } THEN("Calculating masses proactively should work") { REQUIRE( modif == modif.calculateMasses( ok, test_utils_1_letter_modif.msp_polChemDef->getIsotopicDataCstSPtr())); REQUIRE(modif == modif.calculateMasses(ok, nullptr)); REQUIRE(modif.calculateMasses(nullptr)); } THEN("The Modif in the PolChemDef can be retrieved by member name") { ModifCstSPtr modif_csp = test_utils_1_letter_modif.msp_polChemDef->getModifCstSPtrByName(modif.getName()); REQUIRE(modif_csp != nullptr); REQUIRE(modif_csp->getName().toStdString() == "Acetylation"); } THEN( "The Modif in the PolChemDef can be retrieved by explicit name") { ModifCstSPtr modif_csp = test_utils_1_letter_modif.msp_polChemDef->getModifCstSPtrByName(modif.getName()); REQUIRE(modif_csp != nullptr); REQUIRE(modif_csp->getName().toStdString() == "Acetylation"); modif_csp = test_utils_1_letter_modif.msp_polChemDef->getModifCstSPtrByName(""); REQUIRE( modif_csp == nullptr); } THEN("The Modif in the PolChemDef can checked by name") { REQUIRE(modif.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); } } WHEN( "A Modif is created with PolChemDef, inexisting name, correct formula, " "targets and max count") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Notacetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("M;C"); modif.setMaxCount(2); THEN( "The Modif is valid and validates successfully with masses properly " "calculated") { error_list.clear(); REQUIRE(modif.validate(&error_list)); REQUIRE(modif.isValid()); modif.calculateMasses(nullptr); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } THEN("Calculating masses proactively should work") { REQUIRE( modif == modif.calculateMasses( ok, test_utils_1_letter_modif.msp_polChemDef->getIsotopicDataCstSPtr())); } THEN("The Modif in the PolChemDef cannot be retrieved by member name") { ModifCstSPtr modif_csp = test_utils_1_letter_modif.msp_polChemDef->getModifCstSPtrByName(modif.getName()); REQUIRE(modif_csp == nullptr); modif_csp = modif.getFromPolChemDefByName(); REQUIRE(modif_csp == nullptr); REQUIRE(modif.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN); } } } SCENARIO("Construction of a Modif with the copy constructor", "[Modif]") { WHEN("A Modif is created with PolChemDef, name and formula") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("M;C"); modif.setMaxCount(2); THEN( "The Modif is valid and validates successfully with masses properly " "calculated") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(modif.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(modif.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } AND_WHEN("Another Modif is copy-constructed") { Modif new_modif(modif); THEN("It should be identical to the previous one") { REQUIRE(new_modif.getName().toStdString() == modif.getName().toStdString()); REQUIRE(new_modif.getFormula().toStdString() == modif.getFormula().toStdString()); REQUIRE(new_modif.formula(/*with_title*/ false).toStdString() == modif.formula(/*with_title*/ false).toStdString()); REQUIRE(new_modif.getTargets().toStdString() == modif.getTargets().toStdString()); REQUIRE(new_modif.getMaxCount() == modif.getMaxCount()); } THEN("The equality operator should work fine") { REQUIRE(new_modif == modif); REQUIRE_FALSE(new_modif != modif); } } AND_WHEN( "Another Modif is initialized using it with the assignment operator=()") { Modif another_new_modif; another_new_modif = modif; THEN("It should be identical to the previous one") { REQUIRE(another_new_modif.getName().toStdString() == modif.getName().toStdString()); REQUIRE(another_new_modif.getFormula().toStdString() == modif.getFormula().toStdString()); REQUIRE(another_new_modif.formula(/*with_title*/ false).toStdString() == modif.formula(/*with_title*/ false).toStdString()); REQUIRE(another_new_modif.getTargets().toStdString() == modif.getTargets().toStdString()); REQUIRE(another_new_modif.getMaxCount() == modif.getMaxCount()); } THEN("Assigning a Modif to itself should return itself.") { REQUIRE((another_new_modif = another_new_modif) == another_new_modif); } THEN("The equality operator should work fine") { REQUIRE(another_new_modif == modif); REQUIRE_FALSE(another_new_modif != modif); REQUIRE(another_new_modif == another_new_modif); REQUIRE_FALSE(another_new_modif != another_new_modif); } } } } SCENARIO("A Modif can be checked for its targets", "[Modif]") { WHEN( "A Modif is created with PolChemDef, name and formula, then targets are " "set using setter") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("M;C"); modif.setMaxCount(2); THEN("The Modif is checked") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); REQUIRE(modif.doesTargetMonomer("M")); REQUIRE(modif.doesTargetMonomer("C")); REQUIRE_FALSE(modif.doesTargetMonomer("A")); } AND_WHEN("Set targets '*'") { modif.setTargets("*"); THEN("All codes should be targeted") { REQUIRE(modif.doesTargetMonomer("M")); REQUIRE(modif.doesTargetMonomer("C")); REQUIRE(modif.doesTargetMonomer("A")); } } AND_WHEN("Set targets '!'") { modif.setTargets("!"); THEN("No code should be targeted") { REQUIRE_FALSE(modif.doesTargetMonomer("M")); REQUIRE_FALSE(modif.doesTargetMonomer("C")); REQUIRE_FALSE(modif.doesTargetMonomer("A")); } } } } SCENARIO("A Modif can output itself as a string", "[Modif]") { WHEN( "A Modif is created with PolChemDef, name, formula, then set with targets " "and max count") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("M;C"); modif.setMaxCount(2); qDebug() << "The modif: " << modif.toString(); THEN( "The Modif is valid and validates successfully with masses properly " "calculated") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } THEN("The string has the right contents") { REQUIRE(modif.toString().toStdString() == "Acetylation, \"The title\"-H2O+CH3COOH, ;M;C;, 2"); } } } SCENARIO("Modif instances can be constructed using a correct XML element", "[Modif]") { QStringList dom_strings; dom_strings << "mdf" << "name" << "Acetylation" << "formula" << "-H2O+CH3COOH" << "targets" << "K" << "maxcount" << "3"; GIVEN("A proper set of strings, a proper XML element can be crafted") { QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(dom_strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); REQUIRE( document.toString().toStdString() == "\n Acetylation\n -H2O+CH3COOH\n " "K\n 3\n\n"); // qDebug() << "The document:" << document.toString(); WHEN("A Modif instance is allocated using that string and no PolChemDef") { Modif modif(nullptr, mdf_element, 1); THEN("The Modif instance has only partial proper member data") { REQUIRE(modif.getPolChemDefCstSPtr() == nullptr); REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE(modif.getFormula().toStdString() == "-H2O+CH3COOH"); REQUIRE(modif.getTargets().toStdString() == "K"); REQUIRE(modif.getMaxCount() == -1); AND_THEN( "The Modif instance cannot validate successfully because of missing " "PolChemDef and targets string that could not validate because of " "the missing PolChemDef.") { error_list.clear(); REQUIRE_FALSE(modif.validate(&error_list)); REQUIRE_FALSE(modif.isValid()); } } } WHEN("A Modif instance is allocated using that string and valid PolChemDef") { Modif modif = Modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance has proper member data") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE(modif.getFormula().toStdString() == "-H2O+CH3COOH"); REQUIRE(modif.getTargets().toStdString() == ";K;"); REQUIRE(modif.getMaxCount() == 3); AND_THEN("The Modif instance is valid and must validate successfully") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); } } } } } SCENARIO( "Modif instances cannot be correctly constructed using incorrect XML " "elements", "[Modif]") { QStringList dom_strings; dom_strings << "mdf" << "name" << "Acetylation" << "formula" << "-H2O+CH3COOH" << "targets" << "K" << "maxcount" << "3"; // int mdf_element_index = 0; // int name_element_index = 1; // int name_text_index = 2; // int formula_element_index = 3; // int formula_text_index = 4; // int targets_element_index = 5; // int targets_text_index = 6; // int maxcount_element_index = 7; // int maxcount_text_index = 8; GIVEN("An XML element with an incorrect mdf element tag") { QStringList strings(dom_strings); strings[mdf_element_index] = "not_mdf"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == ""); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect name element tag") { QStringList strings(dom_strings); strings[name_element_index] = "not_name"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == ""); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect formula element tag") { QStringList strings(dom_strings); strings[formula_element_index] = "not_formula"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect formula element text") { QStringList strings(dom_strings); strings[formula_text_index] = "+h2O"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect target element tag") { QStringList strings(dom_strings); strings[targets_element_index] = "not-targets"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect target element text") { QStringList strings(dom_strings); strings[targets_text_index] = "$"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect target element text") { QStringList strings(dom_strings); strings[targets_text_index] = "m"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect max count element tag") { QStringList strings(dom_strings); strings[maxcount_element_index] = "not-maxcount"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } GIVEN("An XML element with an incorrect max count element text") { QStringList strings(dom_strings); strings[maxcount_text_index] = "-1"; QDomDocument document = test_utils_1_letter_modif.craftMdfDomDocument(strings); QDomElement mdf_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); WHEN("A Modif instance is allocated using that string") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, mdf_element, 1); THEN("The Modif instance is incorrect") { REQUIRE(modif.getName().toStdString() == "Acetylation"); REQUIRE_FALSE(modif.isValid()); REQUIRE_FALSE(modif.validate(&error_list)); } } } } SCENARIO("A Modif can output itself as an XML element string", "[Modif]") { WHEN("A Modif is created with PolChemDef, name and formula") { Modif modif(test_utils_1_letter_modif.msp_polChemDef, "Acetylation", "\"The title\"-H2O+CH3COOH"); modif.setTargets("M;C"); modif.setMaxCount(2); THEN( "The Modif is valid and validates successfully with masses properly " "calculated") { REQUIRE(modif.isValid()); error_list.clear(); REQUIRE(modif.validate(&error_list)); double mono = 0; double avg = 0; modif.accountMasses(mono, avg, /*times*/ 1); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(42.0105646847, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(42.0369401545, 0.0000000001)); } THEN("The string has the right contents") { QString xml_element_string = modif.formatXmlMdfElement(0, Utils::xmlIndentationToken); QString expected_xml_string = "\n" " Acetylation\n" " \"The title\"-H2O+CH3COOH\n" " ;M;C;\n" " 2\n" "\n" ""; REQUIRE(xml_element_string.toStdString() == expected_xml_string.toStdString()); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Monomer.cpp000664 001750 001750 00000166206 15100504560 021771 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_monomer("protein-1-letter", 1); TestUtils test_utils_3_letters_monomer("protein-3-letters", 1); ErrorList error_list_monomer; SCENARIO( "Monomer instances can be constructed piecemeal starting from totally " "unconfigured", "[Monomer]") { test_utils_1_letter_monomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("An allocated polymer chemistry definition") { WHEN("A Monomer is allocated totally unconfigured") { Monomer monomer; THEN("The Monomer is not valid and does not validate successfully") { REQUIRE_FALSE(monomer.isValid()); REQUIRE(monomer.getName().toStdString() == ""); REQUIRE(monomer.getCode().toStdString() == ""); REQUIRE_FALSE(monomer.checkCodeSyntax()); REQUIRE(monomer.getFormula().toStdString() == ""); REQUIRE(monomer.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } AND_WHEN("The PolChemDef is set") { monomer.setPolChemDefCstSPtr(pol_chem_def_csp); THEN( "The monomer is still not valid and does not validate successfully") { REQUIRE_FALSE(monomer.isValid()); REQUIRE(monomer.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } AND_WHEN("The name is set") { monomer.setName("Tryptophan"); THEN( "The monomer is still not valid and does not validate successfully") { REQUIRE_FALSE(monomer.isValid()); REQUIRE(monomer.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } AND_WHEN("The code is set") { monomer.setCode("W"); THEN( "The monomer is still not valid and does not validate " "successfully") { REQUIRE_FALSE(monomer.isValid()); REQUIRE(monomer.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); REQUIRE(monomer.isKnownByCodeInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } AND_WHEN("The formula is set") { monomer.setFormula( test_utils_1_letter_monomer.m_tryptophanFormulaString); THEN("The monomer is valid and does validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.isKnownByNameInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); REQUIRE(monomer.isKnownByCodeInPolChemDef() == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); REQUIRE(monomer.validate(error_list_monomer)); } } } } } } } } SCENARIO("Monomers can be constructed using a valid XML mnm element", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; QStringList dom_strings{ "mnm", "name", "Glycine", "code", "G", "formula", "C2H3N1O1"}; GIVEN("A proper set of strings, a proper XML element can be crafted") { QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "C2H3N1O1\n\n"); WHEN( "A Monomer instance is allocated using that string but without " "PolChemDef") { Monomer monomer(nullptr, mnm_element, 1); THEN("The Monomer instance is initialized but not the PolChemDef") { REQUIRE(monomer.getName().toStdString() == "Glycine"); REQUIRE(monomer.getCode().toStdString() == "G"); REQUIRE(monomer.getFormula().toStdString() == "C2H3N1O1"); AND_THEN( "The Monomer instance is not valid because cannot " "thoroughly validate successfully because of no PolChemDef") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("A new Monomer is copy-constructed using it") { Monomer new_monomer(monomer); THEN("The new Monomer instance is not valid and does not validate") { REQUIRE_FALSE(new_monomer.isValid()); REQUIRE_FALSE(new_monomer.validate(error_list_monomer)); } } AND_WHEN("A new Monomer is assignment-copied using it") { Monomer new_monomer; new_monomer = monomer; THEN("The new Monomer instance is not valid and does not validate") { REQUIRE_FALSE(new_monomer.isValid()); REQUIRE_FALSE(new_monomer.validate(error_list_monomer)); } } } WHEN("A Monomer instance is allocated with PolChemDef, using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has proper member data") { REQUIRE(monomer.getName().toStdString() == "Glycine"); REQUIRE(monomer.getCode().toStdString() == "G"); REQUIRE(monomer.getFormula().toStdString() == "C2H3N1O1"); AND_THEN("The Monomer instance must validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); } } } } } SCENARIO( "Monomers can be constructed using an invalid XML mnm element but are not " "valid Monomers", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; QStringList dom_strings{ "mnm", "name", "Glycine", "code", "G", "formula", "C2H3N1O1"}; int mnm_element_index = 0; int name_element_index = 1; int name_text_index = 2; int code_element_index = 3; int code_text_index = 4; int formula_element_index = 5; int formula_text_index = 6; GIVEN("A set of strings with erroneous element") { dom_strings[mnm_element_index] = "not_mnm"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous element") { dom_strings[name_element_index] = "not_mame"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous (empty) element's text") { dom_strings[name_text_index] = ""; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n \n G\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous element") { dom_strings[code_element_index] = "not_code"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous element's text") { dom_strings[code_text_index] = "g"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n g\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous element") { dom_strings[formula_element_index] = "not_formula"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "C2H3N1O1\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } GIVEN("A set of strings with erroneous element's text") { dom_strings[formula_text_index] = "h2O"; QDomDocument document = test_utils_1_letter_monomer.craftMnmDomDocument(dom_strings); QDomElement mnm_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // qDebug() << "The document:" << document.toString(); REQUIRE(document.toString().toStdString() == "\n Glycine\n G\n " "h2O\n\n"); WHEN("A Monomer instance is allocated using that string") { Monomer monomer(pol_chem_def_csp, mnm_element, 1); THEN("The Monomer instance has an invalid status") { REQUIRE_FALSE(monomer.isValid()); } } } } SCENARIO( "Monomers can be fully initialized using a XML element out of a " "polymer sequence file", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; // // S // // Phosphorylation // H1O3P1 // * // 1 // // QStringList dom_strings{"monomer", "code", "S", "mdf", "name", "Phosphorylation", "formula", "H1O3P1", "targets", "*", "maxcount", "1"}; // int monomer_element_index = 0; // int code_element_index = 1; // int code_text_index = 2; // int mdf_element_index = 3; // int name_element_index = 4; // int name_text_index = 5; // int formula_element_index = 6; // int formula_text_index = 7; // int targets_element_index = 8; // int targets_text_index = 9; // int maxcount_element_index = 10; // int maxcount_text_index = 11; GIVEN( "A proper set of strings, a proper XML element can be crafted") { QDomDocument document = test_utils_1_letter_monomer.craftMonomerDomDocument(dom_strings); QDomElement monomer_element = document.elementsByTagName(dom_strings[0]).item(0).toElement(); // Use indentation 1 to mimick what happens in XpertMass. qDebug() << "The document:" << document.toString(/*indentation*/ 1); REQUIRE( document.toString(/*indentation*/ 1).toStdString() == "\n S\n \n Phosphorylation\n " "H1O3P1\n *\n " "1\n \n\n"); WHEN( "A Monomer instance is allocated entirely free of data (only the polymer " "chemistry definition is provided to the constructor)") { Monomer monomer(pol_chem_def_csp); THEN("The Monomer instance is not valid and cannot validate") { REQUIRE(monomer.getName().toStdString() == ""); REQUIRE(monomer.getCode().toStdString() == ""); REQUIRE(monomer.getFormula().toStdString() == ""); REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); AND_WHEN( "The monomer instance is asked to render in itself the XML " "element") { REQUIRE( monomer.renderXmlMonomerElement(monomer_element, /*version*/ 1)); THEN( "The Monomer is set correctly, is valid and validates " "successfully.") { REQUIRE(monomer.getName().toStdString() == "Serine"); REQUIRE(monomer.getCode().toStdString() == "S"); REQUIRE(monomer.getFormula().toStdString() == "C3H5N1O2"); REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); AND_THEN( "Calculating the Monomer masses should succeed (not accounting " "modif)") { bool ok = false; REQUIRE( monomer.calculateMasses( ok, monomer.getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Enums::ChemicalEntity::NONE) == monomer); REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(87.0320284060, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(87.0777027179, 0.0000000001)); AND_THEN("Accounting the Monomer masses should succeed") { double mono = 0; double avg = 0; REQUIRE(monomer.accountMasses(mono, avg, 1) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(87.0320284060, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(87.0777027179, 0.0000000001)); } } AND_THEN( "Calculating the Monomer masses should succeed (accounting " "modif)") { bool ok = false; REQUIRE( monomer.calculateMasses( ok, monomer.getPolChemDefCstSPtr()->getIsotopicDataCstSPtr(), Enums::ChemicalEntity::MODIF) == monomer); REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(166.9983592974, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(167.0576323363, 0.0000000001)); AND_THEN("Accounting the Monomer masses should succeed") { double mono = 0; double avg = 0; REQUIRE(monomer.accountMasses(mono, avg, 1) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(166.9983592974, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(167.0576323363, 0.0000000001)); } } } AND_WHEN( "The newly initialized monomer instance prints itself as a " " XML element string") { QString monomer_xml_string = monomer.formatXmlMonomerElement( 0, test_utils_1_letter_monomer.xml_format_indent_string); THEN( "The initial XML element and this one should be " "identical") { // Use indentation 1 to mimick what happens in XpertMass. REQUIRE(monomer_xml_string.toStdString() == document.toString(/*indentation*/ 1).toStdString()); } } } } } } } SCENARIO( "The monomer code needs syntax checking depending on the polymer chemistry " "definition", "[Monomer]") { GIVEN("A one-letter polymer chemistry definition") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; WHEN( "A valid monomer is instantiated with full description (name, code, " "formula)") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); THEN("The monomer should be valid and should validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); } AND_WHEN("An invalid code is set with setCode()") { monomer.setCode("w"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("Ww"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } } } GIVEN("A three-letter polymer chemistry definition") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_3_letters_monomer.msp_polChemDef; ErrorList error_list_monomer; REQUIRE(pol_chem_def_csp->getCodeLength() == 3); REQUIRE(pol_chem_def_csp->getMonomersCstRef().size() == 21); REQUIRE(pol_chem_def_csp->getModifsCstRef().size() == 26); // REQUIRE(pol_chem_def_csp->crossLinkerList().size() == 2); // REQUIRE(pol_chem_def_csp->cleaveSpecList().size() == 8); // REQUIRE(pol_chem_def_csp->fragSpecList().size() == 7); WHEN( "A valid monomer is instantiated with full description (name, code, " "formula)") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "Trp", "C11H10N2O1"); THEN("The monomer should be valid and should validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); } AND_WHEN("An empty code is set with setCode()") { monomer.setCode(""); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("A syntactically valid shorter code is set with setCode()") { monomer.setCode("Tr"); THEN("The monomer becomes valid and does validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); } } AND_WHEN( "Another syntactically valid even shorter code is set with setCode()") { monomer.setCode("T"); THEN("The monomer is still valid and does validate successfully") { REQUIRE(monomer.isValid()); REQUIRE(monomer.validate(error_list_monomer)); } } AND_WHEN("An invalid code is set with setCode()") { monomer.setCode("t"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("t"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("tt"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("ttt"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("tttt"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } AND_WHEN("Another invalid code is set with setCode()") { monomer.setCode("Trpp"); THEN( "The monomer is not valid and does not validate successfully " "anymore") { REQUIRE_FALSE(monomer.isValid()); REQUIRE_FALSE(monomer.validate(error_list_monomer)); } } } } } SCENARIO( "A monomer can be modified multiple times and modifications can be removed " "piecemeal", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; Monomer monomer = Monomer(pol_chem_def_csp, "Lysine", "K", "C6H12N2O1"); Modif methylation(pol_chem_def_csp, "Methylation", "+C1H2"); methylation.setTargets("K"); methylation.setMaxCount(3); REQUIRE(monomer.isModifTarget(methylation)); // We need to store the QUuid strings for the Modif instances. QString first_uuid; QString second_uuid; QString third_uuid; QString fourth_uuid; GIVEN( "A Monomer instance and a Modif that targets that monomer at most three " "times") { WHEN("The monomer is modified once") { qDebug() << "20250428 - Now calling modify with:" << methylation.getName(); first_uuid = monomer.modify(methylation, /* override modif count */ false, error_list_monomer); THEN("The modification should be successful") { REQUIRE_FALSE(first_uuid.isEmpty()); ModifSPtr modif_sp = monomer.getModifForUuid(first_uuid); REQUIRE(modif_sp != nullptr); REQUIRE(modif_sp->getName() == "Methylation"); REQUIRE(*monomer.getModifForUuid(first_uuid) == methylation); REQUIRE(monomer.countModifsByName("Methylation") == 1); } AND_WHEN("The monomer is modified twice") { second_uuid = monomer.modify(methylation, /* override modif count */ false, error_list_monomer); THEN("The modification should be successful") { REQUIRE_FALSE(second_uuid.isEmpty()); REQUIRE(*monomer.getModifForUuid(second_uuid) == methylation); REQUIRE(monomer.countModifsByName("Methylation") == 2); } AND_WHEN("The monomer is modified thrice") { third_uuid = monomer.modify(methylation, /* override modif count */ false, error_list_monomer); THEN("The modification should be successful") { REQUIRE_FALSE(third_uuid.isEmpty()); REQUIRE(*monomer.getModifForUuid(third_uuid) == methylation); REQUIRE(monomer.countModifsByName("Methylation") == 3); } AND_WHEN("The monomer is modified quadrice") { fourth_uuid = monomer.modify(methylation, /* override modif count */ false, error_list_monomer); THEN("The modification should not be successful (> max count)") { REQUIRE(fourth_uuid.isEmpty()); REQUIRE(monomer.getModifForUuid(fourth_uuid) == nullptr); REQUIRE(monomer.countModifsByName("Methylation") == 3); } AND_WHEN( "The monomer is modified quadrice with the override bit set") { fourth_uuid = monomer.modify(methylation, /* override modif count */ true, error_list_monomer); THEN( "The modification should be successful (> max count " "overridden)") { REQUIRE_FALSE(fourth_uuid.isEmpty()); REQUIRE(*monomer.getModifForUuid(fourth_uuid) == methylation); REQUIRE(monomer.countModifsByName("Methylation") == 4); } AND_WHEN("The monomer is unmodified once") { REQUIRE(monomer.unmodify(first_uuid)); THEN("There should remain only three modifications") { REQUIRE(monomer.countModifsByName("Methylation") == 3); } AND_WHEN("The monomer is unmodified once more") { REQUIRE(monomer.unmodify(second_uuid)); THEN("There should remain only two modifications") { REQUIRE(monomer.countModifsByName("Methylation") == 2); } AND_WHEN("The monomer is unmodified once more") { REQUIRE(monomer.unmodify(third_uuid)); THEN("There should remain only one modification") { REQUIRE(monomer.countModifsByName("Methylation") == 1); } AND_WHEN("The monomer is unmodified once more") { REQUIRE(monomer.unmodify(fourth_uuid)); THEN("There should remain no modification") { REQUIRE(monomer.countModifsByName("Methylation") == 0); } } } } } } } } } } } } SCENARIO( "A modified Monomer instance can be copied deeply into a new Monomer " "instance", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN( "A Monomer instance and a heap-allocated Modif that targets that monomer") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); std::unique_ptr oxidation_up = std::make_unique(pol_chem_def_csp, "Oxidation", "+O"); oxidation_up->setTargets("M;Y;W"); oxidation_up->setMaxCount(1); REQUIRE(monomer.isModifTarget(*oxidation_up.get()) == true); WHEN("That monomer is modified with that modif that targets the monomer") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); THEN("The monomer modification process should succeed") { REQUIRE_FALSE(uuid.isEmpty()); REQUIRE(monomer.getModifsCstRef().size() == 1); REQUIRE(monomer.getModifsCstRef() .front() ->getName() .toStdString() == "Oxidation"); } AND_WHEN("A second Monomer is copy-constructed") { Monomer monomer_2(monomer); THEN( "The second monomer should have the same Modif instances as those of " "the first one") { REQUIRE(monomer_2.getPolChemDefCstSPtr() == monomer.getPolChemDefCstSPtr()); REQUIRE(monomer_2.getName().toStdString() == monomer.getName().toStdString()); REQUIRE(monomer_2.getCode().toStdString() == monomer.getCode().toStdString()); REQUIRE(monomer_2.getModifsCstRef().size() == 1); REQUIRE(monomer_2.getModifsCstRef() .front() ->getName() .toStdString() == "Oxidation"); } } AND_WHEN( "A third Monomer is allocated uninitialized and then the first monomer " "is copied using the " "assignment operator") { Monomer monomer_3(pol_chem_def_csp, "NOT_SET", "NOT_SET", "NOT_SET"); monomer_3 = monomer; THEN( "The third monomer should have the same Modif instances as those of " "the first one") { REQUIRE(monomer_3.getPolChemDefCstSPtr() == monomer_3.getPolChemDefCstSPtr()); REQUIRE(monomer_3.getName().toStdString() == monomer.getName().toStdString()); REQUIRE(monomer_3.getCode().toStdString() == monomer.getCode().toStdString()); REQUIRE(monomer_3.getModifsCstRef().size() == 1); REQUIRE(monomer_3.getModifsCstRef() .front() ->getName() .toStdString() == "Oxidation"); } } AND_WHEN( "A fourth Monomer is allocated initialized, and modified with a " "different Modif") { Monomer monomer_4(pol_chem_def_csp, "Serine", "S", "C3H5N1O2"); std::unique_ptr phosphorylation_up = std::make_unique( pol_chem_def_csp, "Phosphorylation", "H1O3P1"); phosphorylation_up->setTargets("S"); phosphorylation_up->setMaxCount(1); // qDebug() << "The modif:" << phosphorylation_up->toString(); REQUIRE(monomer_4.isModifTarget(*phosphorylation_up.get()) == true); REQUIRE(monomer_4.modify(*phosphorylation_up.get(), /* override modif count */ false, error_list_monomer) != nullptr); AND_WHEN("The first monomer is assigned to the fourth monomer") { monomer_4 = monomer; THEN( "The fourth monomer should have the same Modif instances as those " "of the first one") { REQUIRE(monomer_4.getPolChemDefCstSPtr() == monomer_4.getPolChemDefCstSPtr()); REQUIRE(monomer_4.getName().toStdString() == monomer.getName().toStdString()); REQUIRE(monomer_4.getCode().toStdString() == monomer.getCode().toStdString()); REQUIRE(monomer_4.getModifsCstRef().size() == 1); REQUIRE(monomer_4.getModifsCstRef() .front() ->getName() .toStdString() == "Oxidation"); REQUIRE(monomer_4.getModifsCstRef() .front() ->getName() .toStdString() == monomer.getModifsCstRef() .front() ->getName() .toStdString()); } } } } } } SCENARIO( "Monomer instances can be searched into the polymer chemistry definition in " "various ways", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("A fully configured inexisting Monomer") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophanooo", "Z", "C11H10N2O1"); Enums::PolChemDefEntityStatus pol_chem_def_entity_status = Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; THEN( "Checks can be performed with the PolChemDef by using either name or " "code") { pol_chem_def_entity_status = monomer.isKnownByNameInPolChemDef(); REQUIRE(pol_chem_def_entity_status == Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN); MonomerSPtr monomer_csp = monomer.getFromPolChemDefByName(); REQUIRE(monomer_csp == nullptr); monomer_csp = monomer.getPolChemDefCstSPtr()->getMonomerCstSPtrByName( monomer.getName()); REQUIRE(monomer_csp == nullptr); pol_chem_def_entity_status = monomer.isKnownByCodeInPolChemDef(); REQUIRE(pol_chem_def_entity_status == Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN); monomer_csp = monomer.getFromPolChemDefByCode(); REQUIRE(monomer_csp == nullptr); monomer_csp = monomer.getPolChemDefCstSPtr()->getMonomerCstSPtrByCode( monomer.getCode()); REQUIRE(monomer_csp == nullptr); } } GIVEN("A fully configured existing Monomer") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); Enums::PolChemDefEntityStatus pol_chem_def_entity_status = Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN; THEN( "Checks can be performed with the PolChemDef by using either name or " "code") { pol_chem_def_entity_status = monomer.isKnownByNameInPolChemDef(); REQUIRE(pol_chem_def_entity_status == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); MonomerSPtr monomer_csp = monomer.getFromPolChemDefByName(); REQUIRE(monomer_csp != nullptr); REQUIRE(monomer_csp->getName().toStdString() == monomer.getName().toStdString()); monomer_csp = monomer.getPolChemDefCstSPtr()->getMonomerCstSPtrByName( monomer.getName()); REQUIRE(monomer_csp != nullptr); REQUIRE(monomer_csp->getName().toStdString() == monomer.getName().toStdString()); pol_chem_def_entity_status = monomer.isKnownByCodeInPolChemDef(); REQUIRE(pol_chem_def_entity_status == Enums::PolChemDefEntityStatus::ENTITY_KNOWN); monomer_csp = monomer.getFromPolChemDefByCode(); REQUIRE(monomer_csp != nullptr); REQUIRE(monomer_csp->getCode().toStdString() == monomer.getCode().toStdString()); monomer_csp = monomer.getPolChemDefCstSPtr()->getMonomerCstSPtrByCode( monomer.getCode()); REQUIRE(monomer_csp != nullptr); REQUIRE(monomer_csp->getCode().toStdString() == monomer.getCode().toStdString()); } } } SCENARIO( "Monomer instances are properly compared with the ==() and !=() operators", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("A modified Monomer instance") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); std::unique_ptr oxidation_up = std::make_unique(pol_chem_def_csp, "Oxidation", "+O"); oxidation_up->setTargets("W"); // We'll have to modify one monomer twice for the sake of the tests. oxidation_up->setMaxCount(2); REQUIRE(monomer.isModifTarget(*oxidation_up.get()) == true); REQUIRE(monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer) != nullptr); THEN("That Monomer instance is identical to itself") { REQUIRE(monomer == monomer); REQUIRE_FALSE(monomer != monomer); } WHEN("A second Monomer is copy-constructed") { Monomer monomer_2(monomer); THEN("The second monomer should be equal to the first one") { REQUIRE(monomer_2.getPolChemDefCstSPtr() == monomer.getPolChemDefCstSPtr()); REQUIRE(monomer_2.getName().toStdString() == monomer.getName().toStdString()); REQUIRE(monomer_2.getCode().toStdString() == monomer.getCode().toStdString()); REQUIRE(monomer_2.getModifsCstRef().size() == monomer.getModifsCstRef().size()); REQUIRE(monomer_2.getModifsCstRef().front()->getName().toStdString() == "Oxidation"); REQUIRE(monomer_2.getModifsCstRef().front()->getName().toStdString() == monomer.getModifsCstRef().front()->getName().toStdString()); } AND_THEN("The comparison operators should return correct results") { REQUIRE(monomer_2 == monomer); REQUIRE_FALSE(monomer_2 != monomer); } AND_WHEN("The second monomer's name is changed") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); monomer_2.setName("Serine"); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); } } AND_WHEN("The second monomer's code is changed") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); monomer_2.setCode("S"); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); } } AND_WHEN("The second monomer's formula is changed") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); monomer_2.setFormula("H2O"); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); } } AND_WHEN("The second monomer is unmodified") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); monomer_2.unmodify(); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); } } AND_WHEN("The second monomer is modified once more with same modif") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); REQUIRE(monomer_2.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer) != nullptr); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); REQUIRE(monomer_2.getModifsCstRef().size() != monomer.getModifsCstRef().size()); } } AND_WHEN( "The second monomer is first unmodified and then modified with " "different modif") { REQUIRE(monomer_2.getName().toStdString() == "Tryptophan"); monomer_2.unmodify(); // Absolute crap, from a chemical standpoint std::unique_ptr formylation_up = std::make_unique( pol_chem_def_csp, "Formylation", "+HCOOH-H2O"); formylation_up->setTargets("W"); formylation_up->setMaxCount(1); REQUIRE(monomer_2.isModifTarget(*formylation_up.get()) == true); REQUIRE(monomer_2.modify(*formylation_up.get(), /* override modif count */ false, error_list_monomer) != nullptr); THEN("The second and the first monomers are no more equal") { REQUIRE(monomer_2.getModifsCstRef().size() == monomer.getModifsCstRef().size()); REQUIRE(monomer_2 != monomer); REQUIRE_FALSE(monomer_2 == monomer); } } } } } SCENARIO("Monomer instances can be modified using Modif objects", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("A Monomer instance") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); AND_GIVEN( "A Modif instance (heap) that targets that monomer only once (maxCount)") { std::unique_ptr oxidation_up = std::make_unique(pol_chem_def_csp, "Oxidation", "+O"); oxidation_up->setTargets("W"); oxidation_up->setMaxCount(1); REQUIRE(monomer.isModifTarget(*oxidation_up.get()) == true); WHEN("That monomer is modified with that modif") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); THEN("The monomer modification process should succeed") { REQUIRE_FALSE(uuid.isEmpty()); } AND_THEN( "The new Modif in Monomer is equal to that used for the modification " "call") { // Modif modif_a(*oxidation_up.get()); // Modif modif_b(*monomer.getModifsCstRef().front().get()); // REQUIRE(modif_a.getName().toStdString() == // modif_b.getName().toStdString()); REQUIRE(*oxidation_up.get() == *monomer.getModifsCstRef().front().get()); } AND_WHEN( "That monomer is modified again with that modif (maxCount = 1)") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); THEN("The monomer modification process should fail") { REQUIRE(uuid.isEmpty()); } } AND_WHEN("The Modif is changed so as not to target the monomer anymore") { oxidation_up->setTargets("S;T,Y"); THEN("The monomer should not be a target of that Modif anymore") { REQUIRE(monomer.isModifTarget(*oxidation_up.get()) == false); } AND_WHEN("The modification of the monomer is tried again") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); THEN("The modification process should fail") { REQUIRE(uuid.isEmpty()); } } } } } } GIVEN("A Monomer instance") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); AND_GIVEN( "A Modif instance (stack) that targets that monomer only once (maxCount)") { Modif modif(pol_chem_def_csp, "Oxidation", "+O"); modif.setTargets("W"); modif.setMaxCount(1); REQUIRE(monomer.isModifTarget(modif) == true); WHEN("That monomer is modified with that modif") { QString uuid = monomer.modify(modif, /* override modif count */ false, error_list_monomer); THEN("The monomer modification process should succeed") { REQUIRE_FALSE(uuid.isEmpty()); } AND_WHEN( "That monomer is modified again with that modif (maxCount = 1)") { QString uuid = monomer.modify(modif, /* override modif count */ false, error_list_monomer); THEN("The monomer modification process should fail") { REQUIRE(uuid.isEmpty()); } } AND_WHEN("The Modif is changed so as not to target the monomer anymore") { modif.setTargets("S;T,Y"); THEN("The monomer should not be a target of that Modif anymore") { REQUIRE(monomer.isModifTarget(modif) == false); } AND_WHEN("The modification of the monomer is tried again") { QString uuid = monomer.modify(modif, /* override modif count */ false, error_list_monomer); THEN("The modification process should fail") { REQUIRE(uuid.isEmpty()); } } } } } } } SCENARIO("A Monomer that is modified with a Modif has its masses that change", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("An allocated Monomer and a Modif object that targets that monomer") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); std::unique_ptr oxidation_up = std::make_unique(pol_chem_def_csp, "Oxidation", "+O"); oxidation_up->setTargets("M;W"); oxidation_up->setMaxCount(1); REQUIRE(oxidation_up->calculateMasses(nullptr) == true); WHEN( "That Monomer is modified with that Modif and the masses are " "recalculated") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); REQUIRE_FALSE(uuid.isEmpty()); REQUIRE(monomer.getModifsCstRef().size() == 1); REQUIRE(monomer.calculateMasses(nullptr, Enums::ChemicalEntity::MODIF) == true); THEN("The masses of the Monomer do change") { double mono = 0; double avg = 0; REQUIRE_THAT(monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(202.0742275715, 0.0000000001)); REQUIRE_THAT(monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(202.2107092530, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(202.0742275715, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(202.2107092530, 0.0000000001)); } THEN("The monomer should validate the modification also") { REQUIRE(monomer.validate(error_list_monomer)); } AND_WHEN("The monomer is unmodified and the masses are recalculated") { REQUIRE(monomer.isModified()); REQUIRE(monomer.unmodify(uuid)); REQUIRE(monomer.calculateMasses(nullptr, Enums::ChemicalEntity::MODIF)); THEN("The recalculated masses should update") { double mono = 0; double avg = 0; REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); } } } } } SCENARIO("A Monomer cannot be correctly modified with an invalid Modif", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN( "An allocated validated Monomer and an invalid Modif object that targets " "that monomer") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); REQUIRE(monomer.validate(error_list_monomer)); REQUIRE(monomer.isValid()); std::unique_ptr fake_oxidation_up = std::make_unique(pol_chem_def_csp, "FAkeOxidation", "+oO"); fake_oxidation_up->setTargets("M;W"); fake_oxidation_up->setMaxCount(1); REQUIRE(fake_oxidation_up->isValid() == false); REQUIRE_FALSE(fake_oxidation_up->calculateMasses(nullptr)); WHEN( "That Monomer is modified with that Modif and the masses a " "recalculated") { QString uuid = monomer.modify(*fake_oxidation_up.get(), /* override modif count */ false, error_list_monomer); REQUIRE_FALSE(uuid.isEmpty()); REQUIRE(monomer.getModifsCstRef().size() == 1); REQUIRE(monomer.calculateMasses(nullptr, Enums::ChemicalEntity::MODIF)); THEN( "The masses of the Monomer do not change because an invalid formula " "does not translate into masses") { double mono = 0; double avg = 0; REQUIRE_THAT(monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT(monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT(mono, Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT(avg, Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); } THEN("The monomer modified with an incorrect formula should not validate") { REQUIRE_FALSE(monomer.validate(error_list_monomer)); } AND_WHEN("The monomer is unmodified and the masses a recalculated") { REQUIRE(monomer.isModified()); REQUIRE(monomer.unmodify(uuid)); REQUIRE(monomer.calculateMasses(nullptr, Enums::ChemicalEntity::MODIF)); THEN("The recalculated masses should update") { double mono = 0; double avg = 0; REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); } AND_THEN( "The monomer should validate, since it is no more modified with an " "incorrect formula") { REQUIRE(monomer.validate(error_list_monomer)); REQUIRE(monomer.isValid()); } } } } } SCENARIO( "Monomers can be asked to craft an elemental composition formula describing " "them fully", "[Monomer]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_monomer.msp_polChemDef; GIVEN("A fully qualified valid Monomer") { Monomer monomer = Monomer(pol_chem_def_csp, "Tryptophan", "W", "C11H10N2O1"); REQUIRE(monomer.validate(error_list_monomer)); REQUIRE(monomer.isValid()); WHEN("Asked to compute masses and the elemental composition formula") { REQUIRE(monomer.calculateMasses(nullptr)); QString elem_composition = monomer.calculateFormula(Enums::ChemicalEntity::NONE); // qDebug() << "The elemental composition:" << elem_composition; Formula the_formula(elem_composition); REQUIRE_FALSE(the_formula.isValid()); REQUIRE(the_formula.validate(pol_chem_def_csp->getIsotopicDataCstSPtr(), error_list_monomer)); REQUIRE(the_formula.isValid()); REQUIRE(the_formula.getActionFormula().toStdString() == "C11H10N2O1"); AND_WHEN("When calculating masses for that returned formula") { double mono = 0; double avg = 0; bool ok = false; Formula(elem_composition) .accountMasses( ok, pol_chem_def_csp->getIsotopicDataCstSPtr(), mono, avg, 1); REQUIRE(ok); THEN( "All the masses in the monomer and for the chemical composition " "formula should be identical") { double mono = 0; double avg = 0; REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(186.0793129513, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(186.2113005359, 0.0000000001)); } } } AND_GIVEN("A fully qualified valid Modif object") { std::unique_ptr oxidation_up = std::make_unique(pol_chem_def_csp, "Oxidation", "+O"); oxidation_up->setTargets("M;Y;W"); oxidation_up->setMaxCount(1); REQUIRE(oxidation_up->isValid()); REQUIRE(oxidation_up->calculateMasses(nullptr) == true); REQUIRE_THAT(oxidation_up->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(15.9949146202, 0.0000000001)); REQUIRE_THAT(oxidation_up->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(15.9994087171, 0.0000000001)); WHEN("The Monomer is modified with that Modif") { QString uuid = monomer.modify(*oxidation_up.get(), /* override modif count */ false, error_list_monomer); REQUIRE_FALSE(uuid.isEmpty()); REQUIRE(*monomer.getModifForUuid(uuid) == *oxidation_up.get()); THEN("The monomer reports that it is modified") { REQUIRE(monomer.getModifsCstRef().size() == 1); } AND_WHEN("The new masses are calculated") { REQUIRE(monomer.calculateMasses(nullptr, Enums::ChemicalEntity::MODIF)); THEN("These masses are reported correctly") { double mono = 0; double avg = 0; REQUIRE_THAT( monomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(202.0742275715, 0.0000000001)); REQUIRE_THAT( monomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(202.2107092530, 0.0000000001)); REQUIRE(monomer.accountMasses(&mono, &avg) == monomer); REQUIRE_THAT( mono, Catch::Matchers::WithinAbs(202.0742275715, 0.0000000001)); REQUIRE_THAT( avg, Catch::Matchers::WithinAbs(202.2107092530, 0.0000000001)); } } AND_WHEN( "The Monomer's elemental composition is asked for without accounting " "for modifications") { QString elem_composition = monomer.calculateFormula(Enums::ChemicalEntity::NONE); // qDebug() << "The elemental composition:" << elem_composition; Formula the_formula(elem_composition); REQUIRE_FALSE(the_formula.isValid()); REQUIRE( the_formula.validate(pol_chem_def_csp->getIsotopicDataCstSPtr(), /*store*/ true, /*reset*/ false, error_list_monomer)); REQUIRE(the_formula.isValid()); THEN( "The formulas for the modified Monomer and one formula matching " "the modification net chemical should be equal because we did not " "account for Modif") { QString reduced_tryptophan_formula("C11H10N2O1"); REQUIRE(elem_composition.toStdString() == reduced_tryptophan_formula.toStdString()); } } AND_WHEN( "The Monomer's elemental composition is asked for with accounting " "for modifications") { QString elem_composition = monomer.calculateFormula(Enums::ChemicalEntity::MODIF); // qDebug() << "The elemental composition:" << elem_composition; Formula the_formula(elem_composition); REQUIRE_FALSE(the_formula.isValid()); REQUIRE( the_formula.validate(pol_chem_def_csp->getIsotopicDataCstSPtr(), /*store*/ true, /*reset*/ false, error_list_monomer)); REQUIRE(the_formula.isValid()); THEN( "The formulas for the modified Monomer and one formula matching " "the modification result should be equal") { QString oxidized_tryptophan_formula("C11H10N2O2"); REQUIRE(elem_composition.toStdString() == oxidized_tryptophan_formula.toStdString()); } } AND_WHEN("Crippling the Monomer's formula with setFormula(\"+h2o\")") { monomer.setFormula("+h2o"); THEN( "That formula cannot validate and computing the elemental " "composition should fail") { QString elem_composition = monomer.calculateFormula(Enums::ChemicalEntity::NONE); REQUIRE_FALSE(Formula(elem_composition).isValid()); REQUIRE_FALSE( Formula(elem_composition) .validate(pol_chem_def_csp->getIsotopicDataCstSPtr(), error_list_monomer)); } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Oligomer.cpp000664 001750 001750 00000106340 15100504560 022123 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes // #include "tests-config.h" #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_oligomer("protein-1-letter", 1); ErrorList error_list_oligomer; SCENARIO("Oligomer instances are initialized with a reference Polymer", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); THEN("The member data should be set correctly") { REQUIRE(oligomer.getPolymerCstSPtr() == polymer_sp); REQUIRE(oligomer.getName().toStdString() == "Oligomername"); REQUIRE(oligomer.getDescription().toStdString() == "OligomerDescription"); Ionizer the_ionizer = oligomer.getIonizerCstRef(); REQUIRE(the_ionizer == ionizer); REQUIRE_FALSE(oligomer.getIonizerCstRef().isIonized()); REQUIRE_FALSE(oligomer.isModified()); REQUIRE_FALSE(oligomer.isModified(/*deep*/ true)); REQUIRE(oligomer.modifiedMonomerCount() == 0); } THEN( "Masses should not be calculatable with default parameters (no " "IndexRange defined))") { REQUIRE_FALSE(oligomer.calculateMasses()); REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } WHEN( "Defining proper IndexRanges and calculating masses with the " "default CalcOptions member in the Oligomer and the set Ionizer") { std::size_t polymer_size = polymer_sp->size(); REQUIRE(polymer_size == 157); IndexRange whole_polymer_index_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(whole_polymer_index_range); REQUIRE(oligomer.calculateMasses()); THEN("Masses should be calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not be modified") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } } } } } SCENARIO("Oligomer instances can be ionized, deionized and reionized", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); THEN("The member data should be set correctly") { REQUIRE(oligomer.getPolymerCstSPtr() == polymer_sp); REQUIRE(oligomer.getName().toStdString() == "Oligomername"); REQUIRE(oligomer.getDescription().toStdString() == "OligomerDescription"); Ionizer the_ionizer = oligomer.getIonizerCstRef(); REQUIRE(the_ionizer == ionizer); REQUIRE_FALSE(oligomer.getIonizerCstRef().isIonized()); REQUIRE_FALSE(oligomer.isModified()); REQUIRE_FALSE(oligomer.isModified(/*deep*/ true)); REQUIRE(oligomer.modifiedMonomerCount() == 0); } THEN( "Masses should not be calculatable with default parameters (no " "IndexRange defined))") { REQUIRE_FALSE(oligomer.calculateMasses()); REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } WHEN( "Defining proper IndexRanges and calculating masses with the " "default CalcOptions member in the Oligomer and the set Ionizer") { std::size_t polymer_size = polymer_sp->size(); REQUIRE(polymer_size == 157); IndexRange whole_polymer_index_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(whole_polymer_index_range); REQUIRE(oligomer.calculateMasses()); THEN("Masses should be calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not be modified") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } AND_WHEN("Reionizing the Oligomer differently") { REQUIRE(oligomer.deionize() == Enums::IonizationOutcome::DEIONIZED); Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.setLevel(2); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should be updated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(8663.9040045934, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(8669.4221296828, 0.0000000001)); } AND_WHEN("Reionizing the Oligomer differently") { REQUIRE(oligomer.deionize() == Enums::IonizationOutcome::DEIONIZED); Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.setFormula("+Mg"); the_ionizer.setNominalCharge(2); the_ionizer.setLevel(2); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should be updated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(4343.4406106311, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(4346.3596420894, 0.0000000001)); } } } } } } } SCENARIO("Oligomer instances can be configured with different IndexRanges", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer (not ionized yet)") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 47); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN("The masses are calculated with ionization") { REQUIRE(oligomer.calculateMasses()); THEN("Masses should check positively)") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5182.4811523289, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5185.7812440907, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not change") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5182.4811523289, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5185.7812440907, 0.0000000001)); } } AND_WHEN("A new sequence range is added") { IndexRange second_polymer_sequence_range(80, 111); oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .appendIndexRange(second_polymer_sequence_range); // Need to reset the current ionization state. ionizer.forceCurrentStateLevel(0); oligomer.setIonizer(ionizer); REQUIRE(oligomer.calculateMasses()); THEN("Masses should check positively, with ionization accounted for") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(8825.0174030068, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(8830.5762151642, 0.0000000001)); } } } WHEN("The IndexRanges are cleared") { oligomer.getCalcOptionsRef().getIndexRangeCollectionRef().clear(); REQUIRE_FALSE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } AND_WHEN("Start and stop indices are set individually") { oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .setIndexRange(0, 156); REQUIRE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } WHEN("The IndexRanges are cleared") { oligomer.getCalcOptionsRef().getIndexRangeCollectionRef().clear(); REQUIRE_FALSE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); AND_WHEN("Start and stop indices are set individually") { oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .setIndexRange(0, 156); REQUIRE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } } } } } } } SCENARIO( "Oligomer instances can have the mass calculation configured in different " "ways", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } WHEN( "The calculation options are modified to account for monomer " "modifications") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setMonomerEntities(Enums::ChemicalEntity::MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << // chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17406.7665150461, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17417.8162475156, 0.0000000001)); } AND_WHEN( "The calculation options are modified to account for polymer left " "end modification") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() // << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // // qDebug() // << "flags:" // << static_cast(calc_options_ref.getPolymerEntities()) // << chemicalEntityMap[calc_options_ref.getPolymerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17448.7770797308, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17459.8531876702, 0.0000000001)); } AND_WHEN( "The calculation options are modified to account for polymer " "right end modification") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setPolymerEntities( Enums::ChemicalEntity::RIGHT_END_MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() // << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // // qDebug() // << "flags:" // << static_cast(calc_options_ref.getPolymerEntities()) // << chemicalEntityMap[calc_options_ref.getPolymerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17405.7824994624, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17416.8314854577, 0.0000000001)); } } } } } } } } SCENARIO( "Oligomer instances can return Monomer instances at different Sequence " "locations", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN("Specific Monomer instances are requested") { MonomerSPtr monomer_left_end_csp = oligomer.getLeftEndMonomerCstSPtr(); MonomerSPtr monomer_right_end_csp = oligomer.getRightEndMonomerCstSPtr(); THEN("The returned Monomer instances are the right one") { REQUIRE(monomer_left_end_csp->getCode().toStdString() == "M"); REQUIRE(monomer_right_end_csp->getCode().toStdString() == "E"); } } AND_WHEN("Specific other Monomer instances are requested") { MonomerSPtr monomer_left_end_csp = oligomer.getMonomerCstSPtrAt(0); MonomerSPtr monomer_middle_csp = oligomer.getMonomerCstSPtrAt(70); MonomerSPtr monomer_right_end_csp = oligomer.getMonomerCstSPtrAt(156); THEN("The returned Monomer instances are the right one") { REQUIRE(monomer_left_end_csp->getCode().toStdString() == "M"); REQUIRE(monomer_right_end_csp->getCode().toStdString() == "E"); REQUIRE(monomer_middle_csp->getCode().toStdString() == "Y"); } } } } } } SCENARIO("Oligomer instances can be set CrossLink instances", "[Oligomer]") { test_utils_1_letter_oligomer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN("A CrossLink is set between two Monomers") { CrossLinkerCstSPtr cross_linker_disulfide_csp = oligomer.getPolymerCstSPtr() ->getPolChemDefCstSPtr() ->getCrossLinkerCstSPtrByName("DisulfideBond"); REQUIRE(cross_linker_disulfide_csp != nullptr); CrossLinkSPtr cross_link_disulfide_sp = std::make_shared( cross_linker_disulfide_csp, polymer_sp, "Disulfide Bond"); REQUIRE(cross_link_disulfide_sp != nullptr); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_OligomerCollection.cpp000664 001750 001750 00000106630 15100504560 024141 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes // #include "tests-config.h" #include "TestUtils.hpp" #include #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_oligomer_collection("protein-1-letter", 1); ErrorList error_list_oligomer_collection; SCENARIO("OligomerCollection instances are initialized with a reference Polymer", "[OligomerCollection]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; GIVEN("A polymer sequence file loaded from disk") { QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); THEN("The member data should be set correctly") { REQUIRE(oligomer.getPolymerCstSPtr() == polymer_sp); REQUIRE(oligomer.getName().toStdString() == "Oligomername"); REQUIRE(oligomer.getDescription().toStdString() == "OligomerDescription"); Ionizer the_ionizer = oligomer.getIonizerCstRef(); REQUIRE(the_ionizer == ionizer); REQUIRE_FALSE(oligomer.getIonizerCstRef().isIonized()); REQUIRE_FALSE(oligomer.isModified()); REQUIRE_FALSE(oligomer.isModified(/*deep*/ true)); REQUIRE(oligomer.modifiedMonomerCount() == 0); } THEN( "Masses should not be calculatable with default parameters (no " "IndexRange defined))") { REQUIRE_FALSE(oligomer.calculateMasses()); REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } WHEN( "Defining proper IndexRanges and calculating masses with the " "default CalcOptions member in the Oligomer and the set Ionizer") { std::size_t polymer_size = polymer_sp->size(); REQUIRE(polymer_size == 157); IndexRange whole_polymer_index_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(whole_polymer_index_range); REQUIRE(oligomer.calculateMasses()); THEN("Masses should be calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not be modified") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } } } } } SCENARIO("Oligomer instances can be ionized, deionized and reionized", "[Oligomer]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); THEN("The member data should be set correctly") { REQUIRE(oligomer.getPolymerCstSPtr() == polymer_sp); REQUIRE(oligomer.getName().toStdString() == "Oligomername"); REQUIRE(oligomer.getDescription().toStdString() == "OligomerDescription"); Ionizer the_ionizer = oligomer.getIonizerCstRef(); REQUIRE(the_ionizer == ionizer); REQUIRE_FALSE(oligomer.getIonizerCstRef().isIonized()); REQUIRE_FALSE(oligomer.isModified()); REQUIRE_FALSE(oligomer.isModified(/*deep*/ true)); REQUIRE(oligomer.modifiedMonomerCount() == 0); } THEN( "Masses should not be calculatable with default parameters (no " "IndexRange defined))") { REQUIRE_FALSE(oligomer.calculateMasses()); REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } WHEN( "Defining proper IndexRanges and calculating masses with the " "default CalcOptions member in the Oligomer and the set Ionizer") { std::size_t polymer_size = polymer_sp->size(); REQUIRE(polymer_size == 157); IndexRange whole_polymer_index_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(whole_polymer_index_range); REQUIRE(oligomer.calculateMasses()); THEN("Masses should be calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not be modified") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } AND_WHEN("Reionizing the Oligomer differently") { REQUIRE(oligomer.deionize() == Enums::IonizationOutcome::DEIONIZED); Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.setLevel(2); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should be updated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(8663.9040045934, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(8669.4221296828, 0.0000000001)); } AND_WHEN("Reionizing the Oligomer differently") { REQUIRE(oligomer.deionize() == Enums::IonizationOutcome::DEIONIZED); Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.setFormula("+Mg"); the_ionizer.setNominalCharge(2); the_ionizer.setLevel(2); REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should be updated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(4343.4406106311, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(4346.3596420894, 0.0000000001)); } } } } } } } SCENARIO("Oligomer instances can be configured with different IndexRanges", "[Oligomer]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer (not ionized yet)") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 47); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN("The masses are calculated with ionization") { REQUIRE(oligomer.calculateMasses()); THEN("Masses should check positively)") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5182.4811523289, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5185.7812440907, 0.0000000001)); } AND_WHEN( "Ionizing the Oligomer should be performed by first deionizing it " "since it is already ionized") { REQUIRE(oligomer.ionize() == Enums::IonizationOutcome::IONIZED); THEN("Masses should not change") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(5182.4811523289, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(5185.7812440907, 0.0000000001)); } } AND_WHEN("A new sequence range is added") { IndexRange second_polymer_sequence_range(80, 111); oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .appendIndexRange(second_polymer_sequence_range); // Need to reset the current ionization state. ionizer.forceCurrentStateLevel(0); oligomer.setIonizer(ionizer); REQUIRE(oligomer.calculateMasses()); THEN("Masses should check positively, with ionization accounted for") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(8825.0174030068, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(8830.5762151642, 0.0000000001)); } } } WHEN("The IndexRanges are cleared") { oligomer.getCalcOptionsRef().getIndexRangeCollectionRef().clear(); REQUIRE_FALSE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); } AND_WHEN("Start and stop indices are set individually") { oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .setIndexRange(0, 156); REQUIRE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } WHEN("The IndexRanges are cleared") { oligomer.getCalcOptionsRef().getIndexRangeCollectionRef().clear(); REQUIRE_FALSE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT(oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(0, 0.0000000001)); REQUIRE_THAT(oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(0, 0.0000000001)); AND_WHEN("Start and stop indices are set individually") { oligomer.getCalcOptionsRef() .getIndexRangeCollectionRef() .setIndexRange(0, 156); REQUIRE(oligomer.calculateMasses()); THEN("The masses are not calculated anymore") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } } } } } } } } SCENARIO( "Oligomer instances can have the mass calculation configured in different " "ways", "[Oligomer]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } WHEN( "The calculation options are modified to account for monomer " "modifications") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setMonomerEntities(Enums::ChemicalEntity::MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << // chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17406.7665150461, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17417.8162475156, 0.0000000001)); } AND_WHEN( "The calculation options are modified to account for polymer left " "end modification") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setPolymerEntities(Enums::ChemicalEntity::LEFT_END_MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() // << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // // qDebug() // << "flags:" // << static_cast(calc_options_ref.getPolymerEntities()) // << chemicalEntityMap[calc_options_ref.getPolymerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17448.7770797308, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17459.8531876702, 0.0000000001)); } AND_WHEN( "The calculation options are modified to account for polymer " "right end modification") { CalcOptions &calc_options_ref = oligomer.getCalcOptionsRef(); calc_options_ref.setPolymerEntities( Enums::ChemicalEntity::RIGHT_END_MODIF); calc_options_ref.setDeepCalculation(true); // qDebug() // << "flags:" // << static_cast(calc_options_ref.getMonomerEntities()) // << chemicalEntityMap[calc_options_ref.getMonomerEntities()]; // // qDebug() // << "flags:" // << static_cast(calc_options_ref.getPolymerEntities()) // << chemicalEntityMap[calc_options_ref.getPolymerEntities()]; // Reset the ionizer current state to not ionized so that // ionization works ok. Ionizer &the_ionizer = oligomer.getIonizerRef(); the_ionizer.forceCurrentStateLevel(0); REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17405.7824994624, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17416.8314854577, 0.0000000001)); } } } } } } } } SCENARIO( "Oligomer instances can return Monomer instances at different Sequence " "locations", "[Oligomer]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN("Specific Monomer instances are requested") { MonomerSPtr monomer_left_end_csp = oligomer.getLeftEndMonomerCstSPtr(); MonomerSPtr monomer_right_end_csp = oligomer.getRightEndMonomerCstSPtr(); THEN("The returned Monomer instances are the right one") { REQUIRE(monomer_left_end_csp->getCode().toStdString() == "M"); REQUIRE(monomer_right_end_csp->getCode().toStdString() == "E"); } } AND_WHEN("Specific other Monomer instances are requested") { MonomerSPtr monomer_left_end_csp = oligomer.getMonomerCstSPtrAt(0); MonomerSPtr monomer_middle_csp = oligomer.getMonomerCstSPtrAt(70); MonomerSPtr monomer_right_end_csp = oligomer.getMonomerCstSPtrAt(156); THEN("The returned Monomer instances are the right one") { REQUIRE(monomer_left_end_csp->getCode().toStdString() == "M"); REQUIRE(monomer_right_end_csp->getCode().toStdString() == "E"); REQUIRE(monomer_middle_csp->getCode().toStdString() == "Y"); } } } } } } SCENARIO("Oligomer instances can be set CrossLink instances", "[Oligomer]") { test_utils_1_letter_oligomer_collection.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_oligomer_collection.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); GIVEN("A polymer sequence file loaded from disk") { WHEN("Masses are calculated with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } AND_GIVEN("An Oligomer, a IndexRange and an Ionizer") { Oligomer oligomer(polymer_sp, "Oligomername", "OligomerDescription"); Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); oligomer.setIonizer(ionizer); IndexRange first_polymer_sequence_range(0, 156); oligomer.getCalcOptionsRef().setIndexRange(first_polymer_sequence_range); WHEN( "The masses are calculated with ionization and with default " "calculation options (both caps, no modifs for example)") { REQUIRE(oligomer.calculateMasses()); THEN("The masses are calculated correctly") { REQUIRE_THAT( oligomer.getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( oligomer.getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); } AND_WHEN("A CrossLink is set between two Monomers") { CrossLinkerCstSPtr cross_linker_disulfide_csp = oligomer.getPolymerCstSPtr() ->getPolChemDefCstSPtr() ->getCrossLinkerCstSPtrByName("DisulfideBond"); REQUIRE(cross_linker_disulfide_csp != nullptr); CrossLinkSPtr cross_link_disulfide_sp = std::make_shared( cross_linker_disulfide_csp, polymer_sp, "Disulfide Bond"); REQUIRE(cross_link_disulfide_sp != nullptr); } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_PolChemDef.cpp000664 001750 001750 00000063106 15100504560 022316 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Stdlib includes /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include #include #include #include #include #include #include #include namespace MsXpS { namespace libXpertMassCore { ErrorList error_list_pol_chem_def; // In the tests below, we do not use TestUtils because we want // more granularity that what is -by design- offered by TestUtils. SCENARIO( "PolChemDef instantiated using the specification in a PolChemDefSpec " "instance", "[PolChemdef]") { PolChemDefSpec pol_chem_def_spec; PolChemDef pol_chem_def; // We use polymer chemistry definitions copied to the // tests/data/polymer-chemistry-definitions directory. QString test_pol_chem_defs_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR); QString pol_chem_def_name = "protein-1-letter"; QString pol_chem_def_file_base_name = QString("%1.xml").arg(pol_chem_def_name); // Relative is to one of the polChemDefs directory (in system or user // directory) QString pol_chem_def_relative_dir_path = "protein-1-letter"; QString pol_chem_def_relative_file_path = QString("%1/%2") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_absolute_dir_path = QString("%1/%2") .arg(test_pol_chem_defs_dir) .arg(pol_chem_def_relative_dir_path); QString pol_chem_def_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg(pol_chem_def_file_base_name); GIVEN("Constructing an empty PolChemDefSpec") { THEN("The member data are empty") { REQUIRE(pol_chem_def_spec.getName() == ""); REQUIRE(pol_chem_def_spec.getFilePath() == ""); } } AND_GIVEN("Set name and file path") { pol_chem_def_spec.setName(pol_chem_def_file_base_name); pol_chem_def_spec.setFilePath(pol_chem_def_relative_file_path); THEN("The name and file path are set to the member data") { REQUIRE(pol_chem_def_spec.getName().toStdString() == pol_chem_def_file_base_name.toStdString()); REQUIRE(pol_chem_def_spec.getFilePath().toStdString() == pol_chem_def_relative_file_path.toStdString()); } AND_GIVEN("That PolChemDefSpec, allocate a PolChemDef") { PolChemDef pol_chem_def(pol_chem_def_spec); THEN("The PolChemDef name is set to the PolChemDefSpec name") { REQUIRE(pol_chem_def.getName().toStdString() == pol_chem_def_spec.getName().toStdString()); } AND_THEN("The file path is set also") { REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() == pol_chem_def_spec.getFilePath().toStdString()); } AND_THEN("Code length value is 1") { REQUIRE(pol_chem_def.getCodeLength() == -1); } } } } SCENARIO( "PolChemDef version 1 instantiated empty and then got to load the file", "[PolChemDef]") { int version = 1; PolChemDef pol_chem_def; PolChemDefSPtr pol_chem_def_sp = nullptr; // We use polymer chemistry definitions copied to the // tests/data/polymer-chemistry-definitions directory. QString test_pol_chem_defs_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR); QString test_pol_chem_defs_versioned_dir = QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(version); QString pol_chem_def_name = "protein-1-letter"; QString pol_chem_def_file_base_name = QString("%1.xml").arg(pol_chem_def_name); // Relative is to one of the polChemDefs directory (in system or user // directory) QString pol_chem_def_relative_dir_path = "protein-1-letter"; QString pol_chem_def_relative_file_path = QString("%1/%2") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_relative_file_path = QString("%1/%2/%3") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name) .arg("isotopic-data.dat"); QString pol_chem_def_absolute_dir_path = QString("%1/%2") .arg(test_pol_chem_defs_versioned_dir) .arg(pol_chem_def_relative_dir_path); // qDebug() << "The dir: " << pol_chem_def_absolute_dir_path.toStdString() // << std::endl; QString pol_chem_def_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg("isotopic-data.dat"); GIVEN("Constructing an empty PolChemDef") { THEN("The member data are empty") { REQUIRE(pol_chem_def.getName() == ""); REQUIRE(pol_chem_def.getXmlDataFilePath() == ""); } } WHEN("Setting name and XML file path") { pol_chem_def.setName(pol_chem_def_file_base_name); pol_chem_def.setXmlDataFilePath(pol_chem_def_absolute_file_path); THEN("The name and file path are set to the member data") { REQUIRE(pol_chem_def.getName().toStdString() == pol_chem_def_file_base_name.toStdString()); REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() == pol_chem_def_absolute_file_path.toStdString()); REQUIRE(pol_chem_def.getXmlDataDirPath().toStdString() == pol_chem_def_absolute_dir_path.toStdString()); } AND_THEN("The path to the isotopic data can be deduced") { QString isotopic_data_file_path = pol_chem_def.deduceIsotopicDataFilePath(); REQUIRE(isotopic_data_file_path.toStdString() == pol_chem_def_isotopic_data_absolute_file_path.toStdString()); } AND_WHEN("Loading isotopic data from disk file using static function") { // REQUIRE(pol_chem_def.getName().toStdString() == // pol_chem_def_file_base_name.toStdString()); // REQUIRE(pol_chem_def.getXmlDataFilePath().toStdString() == // pol_chem_def_absolute_file_path.toStdString()); // REQUIRE(pol_chem_def.getXmlDataDirPath().toStdString() == // pol_chem_def_absolute_dir_path.toStdString()); pol_chem_def_sp = std::make_shared(pol_chem_def); REQUIRE(pol_chem_def_sp != nullptr); REQUIRE(pol_chem_def_sp.use_count() == 1); std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp); THEN("Count the isotopes that have been loaded") { REQUIRE(isotope_count > 0); // qDebug() << "Loaded " << isotope_count << " isotopes" << std::endl; } WHEN("Loading the polymer chemistry definition using static function") { REQUIRE(pol_chem_def_sp != nullptr); bool result = PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp); THEN("That should not fail") { REQUIRE(result == true); } } } } } SCENARIO("Loading a PolChemDef version 1", "[PolChemDef]") { int version = 1; PolChemDef pol_chem_def; PolChemDefSPtr pol_chem_def_sp = nullptr; // We use polymer chemistry definitions copied to the // tests/data/polymer-chemistry-definitions directory. QString test_pol_chem_defs_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR); QString test_pol_chem_defs_versioned_dir = QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(version); QString pol_chem_def_name = "protein-1-letter"; QString pol_chem_def_file_base_name = QString("%1.xml").arg(pol_chem_def_name); // Relative is to one of the polChemDefs directory (in system or user // directory) QString pol_chem_def_relative_dir_path = "protein-1-letter"; QString pol_chem_def_relative_file_path = QString("%1/%2") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_relative_file_path = QString("%1/%2/%3") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name) .arg("isotopic-data.dat"); QString pol_chem_def_absolute_dir_path = QString("%1/%2") .arg(test_pol_chem_defs_versioned_dir) .arg(pol_chem_def_relative_dir_path); // qDebug() << "The dir: " << pol_chem_def_absolute_dir_path.toStdString() // << std::endl; QString pol_chem_def_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg("isotopic-data.dat"); pol_chem_def_sp = std::make_shared(pol_chem_def); pol_chem_def_sp->setName(pol_chem_def_file_base_name); pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path); GIVEN("Loading isotopic data and polymer chemistry definition") { std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp); bool result = PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp); THEN( "Count the isotopes that have been loaded and check PolChemDef load was " "successful") { REQUIRE(isotope_count > 0); // qDebug() << "Loaded " << isotope_count << " isotopes" << std::endl; REQUIRE(result == true); } AND_THEN("The singular chemical entities are checked (end cap chemistry") { REQUIRE(pol_chem_def_sp->getName().toStdString() == pol_chem_def_name.toStdString()); REQUIRE(pol_chem_def_sp->getLeftCap() .getActionFormula(/*with_title*/ true) .toStdString() == "\"N-term amine\"+H"); REQUIRE(pol_chem_def_sp->getRightCap() .getActionFormula(/*with_title*/ true) .toStdString() == "\"C-term carboxylic acid\"+OH"); } AND_THEN("And the ionization rule is checked") { Ionizer ionizer = pol_chem_def_sp->getIonizerCstRef(); REQUIRE(ionizer.getFormulaCstRef().getActionFormula().toStdString() == "+H"); REQUIRE(ionizer.getNominalCharge() == 1); REQUIRE(ionizer.getLevel() == 1); } AND_THEN("And the code length is checked") { REQUIRE(pol_chem_def_sp->getCodeLength() == 1); } AND_THEN("The Monomer_s are checked") { MonomerSPtr monomer_csp = nullptr; // Because of diaminopimelic acid, not 20, but 21. REQUIRE(pol_chem_def_sp->getMonomersCstRef().size() == 21); monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("G"); REQUIRE(monomer_csp->getName().toStdString() == "Glycine"); REQUIRE(monomer_csp->getCode().toStdString() == "G"); REQUIRE(monomer_csp->getFormula().toStdString() == "C2H3N1O1"); REQUIRE(monomer_csp->validate(error_list_pol_chem_def)); double mono; double avg; REQUIRE(monomer_csp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg)); monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("O"); REQUIRE(monomer_csp->getName().toStdString() == "DiaminoPimelic"); REQUIRE(monomer_csp->getCode().toStdString() == "O"); REQUIRE(monomer_csp->getFormula().toStdString() == "C7H12N2O3"); REQUIRE(monomer_csp->validate(error_list_pol_chem_def) == true); REQUIRE(monomer_csp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg)); monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("D"); REQUIRE(monomer_csp->getName().toStdString() == "Aspartate"); REQUIRE(monomer_csp->getCode().toStdString() == "D"); REQUIRE(monomer_csp->getFormula().toStdString() == "C4H5N1O3"); REQUIRE(monomer_csp->validate(error_list_pol_chem_def) == true); REQUIRE(monomer_csp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg)); monomer_csp = pol_chem_def_sp->getMonomerCstSPtrByCode("P"); REQUIRE(monomer_csp->getName().toStdString() == "Proline"); REQUIRE(monomer_csp->getCode().toStdString() == "P"); REQUIRE(monomer_csp->getFormula().toStdString() == "C5H7N1O1"); REQUIRE(monomer_csp->validate(error_list_pol_chem_def) == true); REQUIRE(monomer_csp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg)); } AND_THEN("The Modif_s are checked") { ModifCstSPtr modif_cspp = nullptr; REQUIRE(pol_chem_def_sp->getModifsCstRef().size() == 26); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("Acetylation"); REQUIRE(modif_cspp->getName().toStdString() == "Acetylation"); REQUIRE(modif_cspp->getFormula().toStdString() == "C2H2O1"); REQUIRE(modif_cspp->getTargets().toStdString() == ";K;"); REQUIRE(modif_cspp->getMaxCount() == 1); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); double mono; double avg; REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("CarboxyMethylation"); REQUIRE(modif_cspp->getName().toStdString() == "CarboxyMethylation"); REQUIRE(modif_cspp->getFormula().toStdString() == "C2H2O2"); REQUIRE(modif_cspp->getTargets().toStdString() == ";C;"); REQUIRE(modif_cspp->getMaxCount() == 1); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("Chromo-H3"); REQUIRE(modif_cspp->getName().toStdString() == "Chromo-H3"); REQUIRE(modif_cspp->getFormula().toStdString() == "-H3"); REQUIRE(modif_cspp->getTargets().toStdString() == ";Y;F;W;"); REQUIRE(modif_cspp->getMaxCount() == 1); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("Formylation"); REQUIRE(modif_cspp->getName().toStdString() == "Formylation"); REQUIRE(modif_cspp->getFormula().toStdString() == "C1O1"); REQUIRE(modif_cspp->getTargets().toStdString() == "*"); REQUIRE(modif_cspp->getMaxCount() == 1); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("Glycylation"); REQUIRE(modif_cspp->getName().toStdString() == "Glycylation"); REQUIRE(modif_cspp->getFormula().toStdString() == "C2H3N1O1"); REQUIRE(modif_cspp->getTargets().toStdString() == ";E;"); REQUIRE(modif_cspp->getMaxCount() == 34); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); modif_cspp = pol_chem_def_sp-> getModifCstSPtrByName("Phosphorylation"); REQUIRE(modif_cspp->getName().toStdString() == "Phosphorylation"); REQUIRE(modif_cspp->getFormula().toStdString() == "H1O3P1"); REQUIRE(modif_cspp->getTargets().toStdString() == ";S;T;Y;"); REQUIRE(modif_cspp->getMaxCount() == 1); REQUIRE(modif_cspp->validate(error_list_pol_chem_def) == true); REQUIRE(modif_cspp->calculateMasses( pol_chem_def_sp->getIsotopicDataCstSPtr(), mono, avg) == true); } AND_THEN("The CrossLinker_s are checked") { CrossLinkerCstSPtr crossLinker_csp = nullptr; ModifCstSPtr modif_csp = nullptr; REQUIRE(pol_chem_def_sp->getCrossLinkersCstRef().size() == 2); crossLinker_csp = pol_chem_def_sp-> getCrossLinkerCstSPtrByName("CFP-chromophore"); REQUIRE(crossLinker_csp->getName().toStdString() == "CFP-chromophore"); REQUIRE(crossLinker_csp->getFormula().toStdString() == ""); REQUIRE(crossLinker_csp->getModifsCstRef().size() == 3); modif_csp = crossLinker_csp->getModifsCstRef().at(0); REQUIRE(modif_csp->getName().toStdString() == "Chromo-O"); modif_csp = crossLinker_csp->getModifsCstRef().at(1); REQUIRE(modif_csp->getName().toStdString() == "Chromo-H3"); modif_csp = crossLinker_csp->getModifsCstRef().at(2); REQUIRE(modif_csp->getName().toStdString() == "Chromo-H"); crossLinker_csp = pol_chem_def_sp-> getCrossLinkerCstSPtrByName("DisulfideBond"); REQUIRE(crossLinker_csp->getName().toStdString() == "DisulfideBond"); REQUIRE(crossLinker_csp->getFormula().toStdString() == ""); REQUIRE(crossLinker_csp->getModifsCstRef().size() == 2); modif_csp = crossLinker_csp->getModifsCstRef().at(0); REQUIRE(modif_csp->getName().toStdString() == "ProtonLoss"); modif_csp = crossLinker_csp->getModifsCstRef().at(1); REQUIRE(modif_csp->getName().toStdString() == "ProtonLoss"); } AND_THEN("The CleavageAgent_s are checked (and the CleavageRules_ if any)") { CleavageAgentCstSPtr cleavage_agent_csp = nullptr; REQUIRE(pol_chem_def_sp->getCleavageAgentsCstRef().size() == 8); cleavage_agent_csp = pol_chem_def_sp-> getCleavageAgentCstSPtrByName("EndoAspN+GluN"); REQUIRE(cleavage_agent_csp->getName().toStdString() == "EndoAspN+GluN"); REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "/D;/E"); cleavage_agent_csp = pol_chem_def_sp-> getCleavageAgentCstSPtrByName("EndoLysC"); REQUIRE(cleavage_agent_csp->getName().toStdString() == "EndoLysC"); REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "K/"); cleavage_agent_csp = pol_chem_def_sp-> getCleavageAgentCstSPtrByName("CyanogenBromide"); REQUIRE(cleavage_agent_csp->getName().toStdString() == "CyanogenBromide"); REQUIRE(cleavage_agent_csp->getPattern().toStdString() == "M/"); CleavageRuleSPtr cleavage_rule_sp = cleavage_agent_csp->getCleavageRuleSPtrByName("Homoseryl"); REQUIRE(cleavage_rule_sp->getName().toStdString() == "Homoseryl"); REQUIRE(cleavage_rule_sp->getRightCode().toStdString() == "M"); REQUIRE( cleavage_rule_sp->getRightFormula().getActionFormula().toStdString() == "-CH2S+O"); } AND_THEN("The FragmentationPathway_s are checked") { FragmentationPathwayCstSPtr fragmentation_pathway_csp = nullptr; REQUIRE(pol_chem_def_sp->getFragmentationPathwaysCstRef().size() == 7); fragmentation_pathway_csp = pol_chem_def_sp-> getFragmentationPathwayCstSPtrByName("a"); REQUIRE(fragmentation_pathway_csp->getName().toStdString() == "a"); REQUIRE(fragmentation_pathway_csp->getFragEnd() == Enums::FragEnd::LE); REQUIRE(fragmentation_pathway_csp->getFormulaCstRef() .getActionFormula() .toStdString() == "-C1O1H1"); REQUIRE(fragmentation_pathway_csp->getMonomerContribution() == 0); REQUIRE(fragmentation_pathway_csp->getComment().toStdString() == ""); fragmentation_pathway_csp = pol_chem_def_sp-> getFragmentationPathwayCstSPtrByName("z"); REQUIRE(fragmentation_pathway_csp->getName().toStdString() == "z"); REQUIRE(fragmentation_pathway_csp->getFragEnd() == Enums::FragEnd::RE); REQUIRE(fragmentation_pathway_csp->getFormulaCstRef() .getActionFormula() .toStdString() == "-N1H1"); REQUIRE(fragmentation_pathway_csp->getMonomerContribution() == 0); REQUIRE(fragmentation_pathway_csp->getComment().toStdString() == "Typically in ECD of protonated ions"); } } } SCENARIO( "Loading the same PolChemDef version 1, writing it as version 2 and checking " "the chemical identity of the two versions", "[PolChemDef]") { int input_version_1 = 1; PolChemDefSPtr pol_chem_def_sp = nullptr; // We use polymer chemistry definitions copied to the // tests/data/polymer-chemistry-definitions directory. QString test_pol_chem_defs_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR); QString test_pol_chem_defs_versioned_dir = QString("%1/version-%2").arg(test_pol_chem_defs_dir).arg(input_version_1); QString pol_chem_def_name = "protein-1-letter"; QString pol_chem_def_file_base_name = QString("%1.xml").arg(pol_chem_def_name); // Relative is to one of the polChemDefs directory (in system or user // directory) QString pol_chem_def_relative_dir_path = "protein-1-letter"; QString pol_chem_def_relative_file_path = QString("%1/%2") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_relative_file_path = QString("%1/%2/%3") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name) .arg("isotopic-data.dat"); QString pol_chem_def_absolute_dir_path = QString("%1/%2") .arg(test_pol_chem_defs_versioned_dir) .arg(pol_chem_def_relative_dir_path); // qDebug() << "The dir: " << pol_chem_def_absolute_dir_path.toStdString() // << std::endl; QString pol_chem_def_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg("isotopic-data.dat"); pol_chem_def_sp = std::make_shared(); pol_chem_def_sp->setName(pol_chem_def_file_base_name); pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path); std::size_t isotope_count = PolChemDef::loadIsotopicData(pol_chem_def_sp); REQUIRE(isotope_count == 288); REQUIRE(PolChemDef::renderXmlPolChemDefFile(pol_chem_def_sp)); GIVEN("A fully loaded PolChemDef version 1 along with its IsotopicData") { QString pol_chem_def_output_dir = QString("%1/%2/%3") .arg(TESTS_OUTPUT_DIR) .arg("polymer-chemistry-definitions") .arg(pol_chem_def_name); // qDebug() << "The output directory:" << pol_chem_def_output_dir; QString pol_chem_def_output_file_name = QString("%1/%2") .arg(pol_chem_def_output_dir) .arg(pol_chem_def_file_base_name); // qDebug() << "The output file:" << pol_chem_def_output_file_name; QDir dir; REQUIRE(dir.mkpath(pol_chem_def_output_dir)); WHEN("Writing that PolChemDef back to file") { pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_output_file_name); THEN("The writing should be succesful") { REQUIRE(pol_chem_def_sp->writeXmlFile()); } } AND_WHEN("Loading the written PolChemDef") { PolChemDefSPtr new_pol_chem_def_sp = nullptr; // We use polymer chemistry definitions copied to the // tests/data/polymer-chemistry-definitions directory. QString test_pol_chem_defs_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_OUTPUT_DIR); QString pol_chem_def_name = "protein-1-letter"; QString pol_chem_def_file_base_name = QString("%1.xml").arg(pol_chem_def_name); // Relative is to one of the polChemDefs directory (in system or user // directory) QString pol_chem_def_relative_dir_path = "protein-1-letter"; QString pol_chem_def_relative_file_path = QString("%1/%2") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_relative_file_path = QString("%1/%2/%3") .arg(pol_chem_def_relative_dir_path) .arg(pol_chem_def_file_base_name) .arg("isotopic-data.dat"); QString pol_chem_def_absolute_dir_path = QString("%1/%2") .arg(test_pol_chem_defs_dir) .arg(pol_chem_def_relative_dir_path); qDebug() << "The dir: " << pol_chem_def_absolute_dir_path; QString pol_chem_def_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg(pol_chem_def_file_base_name); QString pol_chem_def_isotopic_data_absolute_file_path = QString("%1/%2") .arg(pol_chem_def_absolute_dir_path) .arg("isotopic-data.dat"); new_pol_chem_def_sp = std::make_shared(); new_pol_chem_def_sp->setName(pol_chem_def_file_base_name); new_pol_chem_def_sp->setXmlDataFilePath(pol_chem_def_absolute_file_path); qDebug() << "Now loading PolChemDef:" << pol_chem_def_absolute_file_path; std::size_t isotope_count = PolChemDef::loadIsotopicData(new_pol_chem_def_sp); REQUIRE(isotope_count == 288); REQUIRE(PolChemDef::renderXmlPolChemDefFile(new_pol_chem_def_sp)); // qDebug() << "Loaded pol chem def:" << // new_pol_chem_def_sp->getXmlDataFilePath(); REQUIRE(new_pol_chem_def_sp->getIsotopicDataCstSPtr()->size() == 288); THEN( "The first and the second PolChemDef need to be identical in a deep " "comparison way") { REQUIRE( pol_chem_def_sp->isChemicallyEquivalent(*new_pol_chem_def_sp.get())); } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_PolChemDefSpec.cpp000664 001750 001750 00000003534 15100504560 023130 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// libXpertMassCore includes #include /////////////////////// Local includes #include "tests-config.h" #include "TestUtils.hpp" namespace MsXpS { namespace libXpertMassCore { SCENARIO("PolChemDefSpec specifying a polymer chemistry definition", "[PolChemDefSpec]") { PolChemDefSpec pol_chem_def_spec; QString test_data_dir = QString("%1/polymer-chemistry-definitions").arg(TESTS_INPUT_DIR); QString pol_chem_def_name = "protein-1-letter.xml"; QString pol_chem_def_relative_file_path = "protein-1-letter/protein-1-letter.xml"; QString pol_chem_def_absolute_file_path = QString("%1/%2").arg(test_data_dir).arg(pol_chem_def_relative_file_path); GIVEN("Constructing an empty PolChemDefSpec") { THEN("The member data are empty") { REQUIRE(pol_chem_def_spec.getName().toStdString() == ""); REQUIRE(pol_chem_def_spec.getFilePath().toStdString() == ""); } } AND_GIVEN("Set name and file path") { pol_chem_def_spec.setName(pol_chem_def_name); pol_chem_def_spec.setFilePath(pol_chem_def_relative_file_path); THEN("The name and file path are set to the member data") { REQUIRE(pol_chem_def_spec.getName().toStdString() == pol_chem_def_name.toStdString()); REQUIRE(pol_chem_def_spec.getFilePath().toStdString() == pol_chem_def_relative_file_path.toStdString()); } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Polymer.cpp000664 001750 001750 00000051647 15100504560 022006 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// IsoSpec #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes // #include "tests-config.h" #include "TestUtils.hpp" #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_1_letter_polymer("protein-1-letter", 1); ErrorList error_list_polymer; SCENARIO( "Polymer instances can be constructed empty and initialized piecemeal until " "they are valid", "[Polymer]") { test_utils_1_letter_polymer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_polymer.msp_polChemDef; QDateTime now_date_time = QDateTime::currentDateTime(); GIVEN("An allocated polymer chemistry definition") { WHEN("A Polymer is allocated as a SPtr and totally unconfigured") { PolymerSPtr polymer_sp = Polymer::createSPtr(); THEN("The Polymer is not valid and does not validate successfully") { REQUIRE_FALSE(polymer_sp->isValid()); REQUIRE(polymer_sp->getName().toStdString() == ""); REQUIRE(polymer_sp->getCode().toStdString() == ""); REQUIRE(polymer_sp->getAuthor().toStdString() == ""); REQUIRE(polymer_sp->getFilePath().toStdString() == ""); REQUIRE(polymer_sp->getDateTime().toStdString() == ""); REQUIRE(polymer_sp->getSequenceCstRef().getSequence().toStdString() == ""); REQUIRE(polymer_sp->getSequenceCstRef().size() == 0); REQUIRE_FALSE(polymer_sp->getLeftEndModifCstRef().isValid()); REQUIRE_FALSE(polymer_sp->getRightEndModifCstRef().isValid()); REQUIRE_FALSE(polymer_sp->getIonizerCstRef().isValid()); REQUIRE(polymer_sp->getCrossLinksCstRef().size() == 0); } AND_WHEN("Setting PolChemDef and other member data but not sequence") { polymer_sp->setPolChemDefCstSPtr(pol_chem_def_csp); polymer_sp->setName("Test name"); polymer_sp->setCode("Test code"); polymer_sp->setAuthor("Rusconi"); polymer_sp->setFilePath( QString("%1/polymer-sequences/chicken-telokin.mxp") .arg(test_utils_1_letter_polymer.m_testsInputDataDir)); QFileInfo file_info(polymer_sp->getFilePath()); REQUIRE(file_info.exists()); polymer_sp->setDateTime(now_date_time.toString("yyyy-MM-dd:mm:ss")); THEN( "The Polymer still is invalid because the sequence needs to be " "valid.") { REQUIRE_FALSE(polymer_sp->isValid()); } AND_WHEN("Setting the sequence as a string") { polymer_sp->setSequence( test_utils_1_letter_polymer.m_telokinAsMonomerText1Letter); THEN( "The Polymer valid because the sequence at last is there and " "is valid.") { REQUIRE(polymer_sp->isValid()); } } AND_WHEN("Setting the sequence as a Sequence object") { polymer_sp->getSequenceRef().clear(); Sequence sequence( pol_chem_def_csp, test_utils_1_letter_polymer.m_telokinAsMonomerText1Letter); polymer_sp->setSequence(sequence); THEN( "The Polymer valid because the sequence at last is there and " "is valid.") { REQUIRE(polymer_sp->isValid()); } AND_THEN("All the members are set correctly") { REQUIRE(polymer_sp->getPolChemDefCstSPtr() == pol_chem_def_csp); REQUIRE(polymer_sp->getName().toStdString() == "Test name"); REQUIRE(polymer_sp->getCode().toStdString() == "Test code"); REQUIRE(polymer_sp->getAuthor().toStdString() == "Rusconi"); REQUIRE(polymer_sp->getFilePath().toStdString() == QString("%1/polymer-sequences/chicken-telokin.mxp") .arg(test_utils_1_letter_polymer.m_testsInputDataDir) .toStdString()); QFileInfo file_info(polymer_sp->getFilePath()); REQUIRE(polymer_sp->getDateTime().toStdString() == now_date_time.toString("yyyy-MM-dd:mm:ss").toStdString()); REQUIRE(polymer_sp->size() == (std::size_t)test_utils_1_letter_polymer .m_telokinAsMonomerText1Letter.size()); } } } } } } SCENARIO("Polymer instances can be constructed and initialized in one go", "[Polymer]") { test_utils_1_letter_polymer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_polymer.msp_polChemDef; QDateTime now_date_time = QDateTime::currentDateTime(); GIVEN("An allocated polymer chemistry definition") { WHEN("A Polymer is allocated as a SPtr with valid parameters") { PolymerSPtr polymer_sp = Polymer::createSPtr( pol_chem_def_csp, "Test name", "Test code", "Rusconi"); polymer_sp->setFilePath( QString("%1/polymer-sequences/chicken-telokin.mxp") .arg(test_utils_1_letter_polymer.m_testsInputDataDir)); QFileInfo file_info(polymer_sp->getFilePath()); REQUIRE(file_info.exists()); polymer_sp->setDateTime(now_date_time.toString("yyyy-MM-dd:mm:ss")); polymer_sp->setSequence( test_utils_1_letter_polymer.m_telokinAsMonomerText1Letter); THEN("The Polymer is valid and all the members are set correctly") { REQUIRE(polymer_sp->getPolChemDefCstSPtr() == pol_chem_def_csp); REQUIRE(polymer_sp->getName().toStdString() == "Test name"); REQUIRE(polymer_sp->getCode().toStdString() == "Test code"); REQUIRE(polymer_sp->getAuthor().toStdString() == "Rusconi"); REQUIRE(polymer_sp->getFilePath().toStdString() == QString("%1/polymer-sequences/chicken-telokin.mxp") .arg(test_utils_1_letter_polymer.m_testsInputDataDir) .toStdString()); QFileInfo file_info(polymer_sp->getFilePath()); REQUIRE(polymer_sp->getDateTime().toStdString() == now_date_time.toString("yyyy-MM-dd:mm:ss").toStdString()); REQUIRE(polymer_sp->size() == (std::size_t)test_utils_1_letter_polymer .m_telokinAsMonomerText1Letter.size()); } THEN("The end modifications are invalid because not defined") { REQUIRE_FALSE(polymer_sp->getLeftEndModifCstRef().isValid()); REQUIRE_FALSE( polymer_sp->getLeftEndModifCstRef().validate(error_list_polymer)); REQUIRE_FALSE(polymer_sp->getRightEndModifCstRef().isValid()); REQUIRE_FALSE( polymer_sp->getRightEndModifCstRef().validate(error_list_polymer)); } AND_WHEN("Setting the left end modification by name") { REQUIRE(polymer_sp->setLeftEndModifByName("Acetylation")); THEN("The left end modif becomes valid") { REQUIRE(polymer_sp->getLeftEndModifCstRef().isValid()); REQUIRE( polymer_sp->getLeftEndModifCstRef().validate(error_list_polymer)); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); } AND_WHEN("Setting the right end modification by name") { REQUIRE(polymer_sp->setRightEndModifByName("AmidationGlu")); THEN("The right end modif becomes valid") { REQUIRE(polymer_sp->getRightEndModifCstRef().isValid()); REQUIRE(polymer_sp->getRightEndModifCstRef().validate( error_list_polymer)); REQUIRE( polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); } } } WHEN("Setting the Ionizer (monoprotonation)") { Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); polymer_sp->setIonizer(ionizer); THEN("The Ionizer must be set correctly as a member datum") { REQUIRE(polymer_sp->getIonizerCstRef() .getFormulaCstRef() .getActionFormula(/*with_title*/ false) .toStdString() == "+H"); REQUIRE(polymer_sp->getIonizerCstRef().getLevel() == 1); REQUIRE(polymer_sp->getIonizerCstRef().getNominalCharge() == 1); REQUIRE(polymer_sp->getIonizerCstRef().charge() == 1); } } } } } SCENARIO("Polymer instances can be modified on their left and/or right ends", "[Polymer]") { test_utils_1_letter_polymer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_polymer.msp_polChemDef; QDateTime now_date_time = QDateTime::currentDateTime(); GIVEN("An allocated polymer chemistry definition") { WHEN( "A Polymer is allocated as a SPtr with valid parameters and a sequence " "is set") { PolymerSPtr polymer_sp = Polymer::createSPtr( pol_chem_def_csp, "Test name", "Test code", "Rusconi", test_utils_1_letter_polymer.m_telokinAsMonomerText1Letter); THEN("The left end modifs are not yet initialized and are invalid") { REQUIRE_FALSE(polymer_sp->getLeftEndModifCstRef().isValid()); REQUIRE_FALSE(polymer_sp->getRightEndModifCstRef().isValid()); } AND_WHEN("That polymer is modified on its left end using Modif name") { polymer_sp->setLeftEndModifByName("Acetylation"); THEN("The Modif is properly set and valid") { REQUIRE(polymer_sp->getLeftEndModifCstRef().isValid()); ModifCstSPtr modif_csp = pol_chem_def_csp->getModifCstSPtrByName("Acetylation"); REQUIRE(modif_csp != nullptr); REQUIRE(*modif_csp.get() == polymer_sp->getLeftEndModifCstRef()); } AND_WHEN("That polymer is modified on its right end using Modif name") { polymer_sp->setRightEndModifByName("AmidationGlu"); THEN("The Modif is properly set and valid") { REQUIRE(polymer_sp->getRightEndModifCstRef().isValid()); ModifCstSPtr modif_csp = pol_chem_def_csp->getModifCstSPtrByName("AmidationGlu"); REQUIRE(modif_csp != nullptr); REQUIRE(*modif_csp.get() == polymer_sp->getRightEndModifCstRef()); } } } } } } SCENARIO("Polymer instances can be initialized from files on disk", "[Polymer]") { test_utils_1_letter_polymer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_polymer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("chicken-telokin.mxp"); GIVEN("A polymer sequence file loaded from disk") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->isLeftEndModified()); REQUIRE(polymer_sp->getLeftEndModifCstRef().getName().toStdString() == "Acetylation"); REQUIRE(polymer_sp->getRightEndModifCstRef().getName().toStdString() == "AmidationGlu"); REQUIRE(polymer_sp->isRightEndModified()); REQUIRE(polymer_sp->size() == 157); REQUIRE(polymer_sp->getSequenceCstRef().hasModifiedMonomer(0, 156)); std::vector indices = polymer_sp->getSequenceCstRef().modifiedMonomerIndices(0, 156); REQUIRE(indices.size() == 1); REQUIRE(polymer_sp->getSequenceCstRef() .getMonomerCstRPtrAt(indices.front()) ->getModifsCstRef() .front() ->getName() .toStdString() == "Phosphorylation"); AND_GIVEN("Calculation options with default parameters") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, 156)); WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } AND_WHEN( "An Ionizer is setup, set to the Polymer and then the latter is " "ionized") { Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"protonation\"+H", /*charge*/ 1, /*level*/ 1); polymer_sp->setIonizer(ionizer); polymer_sp->ionize(); THEN("The masses are updated as expected and the Polymer is ionized") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17326.8001841546, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17337.8363178973, 0.0000000001)); REQUIRE(polymer_sp->getIonizerCstRef().isIonized()); } AND_WHEN("The Polymer is deionized") { polymer_sp->deionize(); THEN( "The masses are updated as expected and the Polymer is no more " "ionized") { // qDebug() << "HERE mono" << // polymer_sp->getMassRef(Enums::MassType::MONO); qDebug() << "HERE avg" // << polymer_sp->getMassRef(Enums::MassType::AVG); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); REQUIRE_FALSE(polymer_sp->getIonizerCstRef().isIonized()); } AND_WHEN( "A complex Mg2+-based ionizer is set up to ionize the Polymer") { Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"magnesiumylation\"+Mg", /*charge*/ 2, /*level*/ 2); polymer_sp->setIonizer(ionizer); polymer_sp->ionize(); THEN( "The masses are updated as expected and the Polymer is " "ionized") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(4343.4406106311, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(4346.3596420894, 0.0000000001)); REQUIRE(polymer_sp->getIonizerCstRef().isIonized()); REQUIRE(polymer_sp->getIonizerCstRef().charge() == 4); } AND_WHEN( "Molecular masses are asked for, the polymer is deionized") { Ionizer ionizer(pol_chem_def_csp->getIsotopicDataCstSPtr(), "\"magnesiumylation\"+Mg", /*charge*/ 2, /*level*/ 2); polymer_sp->setIonizer(ionizer); polymer_sp->ionize(); THEN( "The masses are updated as expected and the Polymer is " "ionized") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(4343.4406106311, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(4346.3596420894, 0.0000000001)); REQUIRE(polymer_sp->getIonizerCstRef().isIonized()); REQUIRE(polymer_sp->getIonizerCstRef().charge() == 4); AND_THEN("Asking for molecular masses returns proper values") { double mol_mass_mono; double mol_mass_avg; polymer_sp->molecularMasses(mol_mass_mono, mol_mass_avg); // The masses *IN* the polymer do not change. REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(4343.4406106311, 0.0000000001)); REQUIRE_THAT(polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(4346.3596420894, 0.0000000001)); // The masses returned are correct. REQUIRE_THAT(mol_mass_mono, Catch::Matchers::WithinAbs(17325.7923591224, 0.0000000001)); REQUIRE_THAT(mol_mass_avg, Catch::Matchers::WithinAbs(17336.8283764289, 0.0000000001)); } } } } } } } } } } SCENARIO("Construction of a Polymer with 7 CrossLink instances right from file", "[Polymer]") { test_utils_1_letter_polymer.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_1_letter_polymer.msp_polChemDef; QString polymer_file_path = QString("%1/polymer-sequences/%2") .arg(TESTS_INPUT_DIR) .arg("kunitz-inhibitor-human-cross-links.mxp"); GIVEN("A Polymer instance created by loading an XML file") { PolymerSPtr polymer_sp = Polymer::createSPtr(pol_chem_def_csp); REQUIRE(polymer_sp->renderXmlPolymerFile(polymer_file_path)); REQUIRE(polymer_sp->size() == 352); WHEN("Configuring calculation options NOT to account for cross-links") { CalcOptions calc_options(/*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::NONE, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, polymer_sp->size() - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(38973.9813242497, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(38999.3128988044, 0.0000000001)); } AND_WHEN("Configuring calculation options to account for cross-links") { CalcOptions calc_options( /*deep_calculation*/ false, /*mass_type*/ Enums::MassType::BOTH, /*capping*/ Enums::CapType::BOTH, /*monomer_entities*/ Enums::ChemicalEntity::CROSS_LINKER, /*polymer_entities*/ Enums::ChemicalEntity::NONE); calc_options.setIndexRange(IndexRange(0, polymer_sp->size() - 1)); AND_WHEN("The Polymer is asked to compute its masses") { REQUIRE(polymer_sp->calculateMasses(calc_options, /*reset*/ true)); THEN("The masses are checked and are as expected") { REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::MONO), Catch::Matchers::WithinAbs(38959.8717737979, 0.0000000001)); REQUIRE_THAT( polymer_sp->getMass(Enums::MassType::AVG), Catch::Matchers::WithinAbs(38985.2017182470, 0.0000000001)); } } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_Sequence.cpp000664 001750 001750 00000106267 15100504560 022126 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include #include namespace MsXpS { namespace libXpertMassCore { TestUtils test_utils_sequence_1_letter("protein-1-letter", 1); ErrorList error_list_sequence; SCENARIO("Sequence object can be constructed empty", "[Sequence]") { test_utils_sequence_1_letter.initializeXpertmassLibrary(); PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; WHEN("Constructing an empty Sequence") { qDebug() << "Allocate empty sequence."; Sequence sequence; THEN("The object is invalid") { REQUIRE_FALSE(sequence.isValid()); } AND_WHEN("Setting the PolChemDef and then a sequence as 1-letter code text") { qDebug() << "Will set the pol chem def."; sequence.setPolChemDefCstSPtr(pol_chem_def_csp); REQUIRE(sequence.getPolChemDef() != nullptr); REQUIRE(sequence.getPolChemDef().get() != nullptr); std::vector failing_indices; qDebug() << "Will set the sequence."; sequence.setSequence( test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter, failing_indices); THEN( "The sequence is valid because both PolChemDef and a proper sequence " "are defined.") { REQUIRE_FALSE(failing_indices.size()); REQUIRE(sequence.isValid()); } } } } SCENARIO("Sequence objects can be constructed in one go", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A fully qualified Sequence object") { Sequence sequence( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); THEN("The Sequence is valid") { REQUIRE(sequence.isValid()); } } } SCENARIO("Sequence objects can be copy-constructed", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A fully qualified Sequence object") { Sequence sequence( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); THEN("The Sequence is valid") { REQUIRE(sequence.validate(error_list_sequence)); REQUIRE(sequence.isValid()); REQUIRE(sequence.size() == 157); } WHEN("A new Sequence is copy-constructed") { REQUIRE(sequence.size() == 157); Sequence new_sequence(sequence); REQUIRE(new_sequence.size() == 157); THEN("The new Sequence is valid and is identical to the initial one") { REQUIRE(new_sequence.isValid()); REQUIRE(new_sequence == sequence); } } } } SCENARIO("Sequence objects can be assignment-configured", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A fully qualified Sequence instance") { Sequence sequence( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); THEN("The Sequence is valid") { REQUIRE(sequence.validate(error_list_sequence)); REQUIRE(sequence.isValid()); } WHEN("Instantiating an empty sequence and assigning to it the previous one") { Sequence new_sequence; new_sequence = sequence; REQUIRE((sequence = sequence) == sequence); REQUIRE((new_sequence = new_sequence) == new_sequence); THEN("The new Sequence is valid and is identical to the initial one") { REQUIRE(new_sequence.validate(error_list_sequence)); REQUIRE(new_sequence.isValid()); REQUIRE(new_sequence == sequence); } THEN("The sequence can be gotten back ") { REQUIRE(sequence.getSequence().toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter .toStdString()); REQUIRE(new_sequence.getSequence().toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter .toStdString()); } } } } SCENARIO("Sequence objects can be tested for (in)equality", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A fully qualified Sequence object and a copy of it") { Sequence sequence( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); Sequence new_sequence(sequence); THEN("The sequences are identical and can be tested for (in)equality") { REQUIRE(new_sequence == sequence); REQUIRE_FALSE(new_sequence != sequence); } THEN( "If the sequence is compared to itself the proper result must be " "returned") { REQUIRE(sequence == sequence); REQUIRE_FALSE(sequence != sequence); } AND_WHEN("A copy of that sequence is made using the assignment operator") { Sequence new_sequence; new_sequence = sequence; THEN("Both sequences can be compared") { REQUIRE(new_sequence == sequence); REQUIRE_FALSE(new_sequence != sequence); } } } } SCENARIO( "Sequence instances can be iterated into code by code (1-letter-code case)", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A newly empty allocated sequence") { Sequence sequence_1(pol_chem_def_csp, ""); WHEN("Setting sequence as 1-letter code text") { std::vector failing_indices; sequence_1.setSequence( test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter, failing_indices); THEN( "The sequence cannot be gotten back because makeMonomerList() must be " "called first") { REQUIRE(sequence_1.getSequence().toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter .toStdString()); } THEN("Asking for next code starting at index 0 should work") { QString sequence = sequence_1.getSequence(); QString next_code; std::size_t last_parsed_code_char_index = 0; QString error; // All the monomer codes are 1-letter-long. std::size_t parsed_code_length = sequence_1.nextCode( sequence, next_code, last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "M"); // The very first index will be returned because we have a 1-letter // code string and the very first code is at index 0.. REQUIRE(parsed_code_length == 1); REQUIRE(last_parsed_code_char_index == 0); REQUIRE(!error.size()); AND_THEN( "Asking for next code using and incremented index, should work") { int parsed_code_length = sequence_1.nextCode( sequence, next_code, ++last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "A"); REQUIRE(parsed_code_length == 1); REQUIRE(last_parsed_code_char_index == 1); REQUIRE(!error.size()); } } } } GIVEN("A newly empty allocated sequence") { Sequence sequence_1(pol_chem_def_csp, ""); WHEN("Setting sequence as 1-letter code text") { std::vector failing_indices; sequence_1.setSequence( test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter, failing_indices); THEN("The sequence can be gotten back") { REQUIRE(sequence_1.getSequence().toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter .toStdString()); AND_THEN("Asking for next code starting at index 0 should work") { QString sequence = sequence_1.getSequence(); QString next_code; std::size_t last_parsed_code_char_index = 0; QString error; // All the monomer codes are 1-letter-long. int parsed_code_length = sequence_1.nextCode( sequence, next_code, last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "M"); // The very first index will be returned because we have a 1-letter // code string and the very first code is at index 0.. REQUIRE(parsed_code_length == 1); REQUIRE(last_parsed_code_char_index == 0); REQUIRE(!error.size()); AND_THEN( "Asking for next code using an incremented index, should work") { int parsed_code_length = sequence_1.nextCode( sequence, next_code, ++last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "A"); REQUIRE(parsed_code_length == 1); REQUIRE(last_parsed_code_char_index == 1); REQUIRE(!error.size()); } } } } } } SCENARIO( "Sequence instances can be iterated into code by code (3-letter-code case)", "[Sequence]") { TestUtils test_utils_sequence_3_letter("protein-3-letters", 1); PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_3_letter.msp_polChemDef; GIVEN("A newly empty allocated sequence") { Sequence sequence_1(pol_chem_def_csp, ""); WHEN("Setting sequence as 3-letter code text") { std::vector failing_indices; sequence_1.setSequence( test_utils_sequence_3_letter.m_telokinAsMonomerText3Letters, failing_indices); THEN("The sequence can be checked") { REQUIRE(sequence_1.getSequence().toStdString() == test_utils_sequence_3_letter.m_telokinAsMonomerText3Letters .toStdString()); AND_THEN("Asking for next code starting at index 0 should work") { QString sequence = sequence_1.getSequence(); QString next_code; std::size_t last_parsed_code_char_index = 0; QString error; // All the monomer codes are 3-letter-long. int parsed_code_length = sequence_1.nextCode( sequence, next_code, last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "Met"); // The very first index will be returned because we have a 1-letter // code string and the very first code is at index 0.. REQUIRE(parsed_code_length == 3); REQUIRE(last_parsed_code_char_index == 2); REQUIRE(!error.size()); AND_THEN( "Asking for next code using and incremented index, should work") { int parsed_code_length = sequence_1.nextCode( sequence, next_code, ++last_parsed_code_char_index, error); REQUIRE(next_code.toStdString() == "Ala"); REQUIRE(parsed_code_length == 3); REQUIRE(last_parsed_code_char_index == 5); REQUIRE(!error.size()); } } } } } } SCENARIO("Sequence_s can be copied and compared", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("Newly allocated sequences with string sequences during construction") { Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); Sequence sequence_2( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinNtermPeptideAsMonomerText1Letter); REQUIRE_FALSE(sequence_1 == sequence_2); REQUIRE(sequence_1 != sequence_2); WHEN("A new Sequence is either copy-constructed or assigned") { Sequence sequence_3(sequence_1); Sequence sequence_4 = sequence_1; THEN("The two Sequence objects should be identical (monomer text-wise)") { REQUIRE(sequence_3 == sequence_1); REQUIRE_FALSE(sequence_3 != sequence_1); REQUIRE(sequence_4 == sequence_1); REQUIRE_FALSE(sequence_4 != sequence_1); } AND_WHEN("A Sequence is either assigned a different sequence") { sequence_4 = sequence_2; THEN("The two Sequence objects should be identical (monomer text-wise)") { REQUIRE(sequence_4 == sequence_2); REQUIRE_FALSE(sequence_4 != sequence_2); } } } } GIVEN("Two Sequence instances differing by only one Monomer") { QString sequence_string_1 = "MAMISGMSGRKAS"; Sequence sequence_1(pol_chem_def_csp, sequence_string_1); QString sequence_string_2 = "MAMISGWSGRKAS"; Sequence sequence_2(pol_chem_def_csp, sequence_string_2); REQUIRE_FALSE(sequence_1 == sequence_2); REQUIRE(sequence_1 != sequence_2); WHEN("A new Sequence is either copy-constructed or assigned") { Sequence sequence_3(sequence_1); Sequence sequence_4 = sequence_1; THEN( "The two Sequence objects should be identical (monomer " "text-wise)") { REQUIRE(sequence_3 == sequence_1); REQUIRE_FALSE(sequence_3 != sequence_1); REQUIRE(sequence_4 == sequence_1); REQUIRE_FALSE(sequence_4 != sequence_1); } AND_WHEN("A Sequence is either assigned a different sequence") { sequence_4 = sequence_2; THEN( "The two Sequence objects should be identical (monomer " "text-wise)") { REQUIRE(sequence_4 == sequence_2); REQUIRE_FALSE(sequence_4 != sequence_2); } } } } } SCENARIO("Monomers can be retrieved by pointer from a Sequence", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); GIVEN("A newly allocated sequence with a string sequence during construction") { WHEN("Retrieving the index of a inexistent Monomer fails") { MonomerSPtr monomer_sp = std::make_shared(pol_chem_def_csp, "NOT_SET", "NOT_SET"); bool ok = false; REQUIRE(sequence_1.monomerIndex(monomer_sp, ok) == 0); REQUIRE_FALSE(ok); } WHEN("Retrieving the Monomer at a given index as MonomerCstSPtr") { MonomerSPtr monomer_csp = sequence_1.getMonomerCstSPtrAt(4); THEN("The identity of the monomer can be checked") { REQUIRE(monomer_csp->getCode().toStdString() == "S"); AND_THEN("Retrieving the index of that Monomer using the raw pointer") { bool ok = false; REQUIRE(sequence_1.monomerIndex(monomer_csp.get(), ok) == 4); REQUIRE(ok); } } } } } SCENARIO("A motif can be searched in a Sequence", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A newly allocated sequence with a string sequence during construction") { Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); QString monomer_text = sequence_1.getSequence(); REQUIRE(sequence_1.size() == static_cast(monomer_text.size())); std::size_t search_index_1 = 0; std::size_t search_index_2 = 0; std::size_t search_index_3 = 0; std::size_t search_index_4 = 0; std::size_t search_index_5 = 0; std::size_t search_index_6 = 0; std::size_t search_index_7 = 0; Sequence motif_sequence_1_occurrence(pol_chem_def_csp, "MAMISGM"); Sequence motif_sequence_6_occurrences(pol_chem_def_csp, "EE"); WHEN("A single-copy existing motif is searched at a bad index (index: -1)") { std::size_t bad_index = -1; int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, bad_index); THEN("The motif cannot be found") { REQUIRE(result_1 == -1); } } WHEN( "A single-copy existing motif is searched at a bad index (index: >= " "size())") { std::size_t bad_index = test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter.size(); int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, bad_index); THEN("The motif cannot be found") { REQUIRE(result_1 == -1); } } WHEN("An empty motif is searched") { Sequence empty_motif(pol_chem_def_csp, ""); int result_1 = sequence_1.findForwardMotif(empty_motif, search_index_1); THEN("The motif cannot be found") { REQUIRE(result_1 == 0); } } WHEN( "A single-copy existing motif is searched with an index that results " "out-of-bounds") { std::size_t bad_index = test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter.size() - 1; int result_1 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, bad_index); THEN("The motif cannot be found") { REQUIRE(result_1 == 0); } } WHEN("A single-copy existing motif is searched") { int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_1); search_index_2 = search_index_1 + motif_sequence_1_occurrence.size(); int result_2 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_2); THEN("The motif should be found only once") { REQUIRE(result_1 == 1); REQUIRE(search_index_1 == 0); REQUIRE(result_2 == 0); } AND_WHEN("A a seven-copy existing motif is searched") { search_index_1 = 0; // qDebug() << "search_index_1: " << search_index_1 << std::endl; // qDebug() << "search_index_2: " << search_index_2 << std::endl; int result_1 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_1); search_index_2 = search_index_1 + motif_sequence_6_occurrences.size(); int result_2 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_2); search_index_3 = search_index_2 + motif_sequence_6_occurrences.size(); int result_3 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_3); search_index_4 = search_index_3 + motif_sequence_6_occurrences.size(); int result_4 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_4); search_index_5 = search_index_4 + motif_sequence_6_occurrences.size(); int result_5 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_5); search_index_6 = search_index_5 + motif_sequence_6_occurrences.size(); int result_6 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_6); search_index_7 = search_index_6 + motif_sequence_6_occurrences.size(); int result_7 = sequence_1.findForwardMotif(motif_sequence_6_occurrences, search_index_7); THEN("The motif should be found seven times") { REQUIRE(result_1 == 1); REQUIRE(search_index_1 == 33); REQUIRE(result_2 == 1); REQUIRE(search_index_2 == 37); REQUIRE(result_3 == 1); REQUIRE(search_index_3 == 96); REQUIRE(result_4 == 1); REQUIRE(search_index_4 == 148); REQUIRE(result_5 == 1); REQUIRE(search_index_5 == 151); REQUIRE(result_6 == 1); REQUIRE(search_index_6 == 153); REQUIRE(result_7 == 1); REQUIRE(search_index_7 == 155); } } } } } SCENARIO("A number of checks might be performed on a Sequence", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A newly allocated sequence with a string sequence during construction") { Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); QString monomer_text = sequence_1.getSequence(); REQUIRE(monomer_text.size() == test_utils_sequence_1_letter.m_telokinSequenceMonomerCount); REQUIRE(static_cast(monomer_text.size()) == sequence_1.size()); std::size_t tested_index = 0; THEN("It is possible to check if an index is inbound or not") { REQUIRE(sequence_1.isInBound(tested_index) == true); tested_index = monomer_text.size() / 2; REQUIRE(sequence_1.isInBound(tested_index) == true); tested_index = monomer_text.size() - 1; REQUIRE(sequence_1.isInBound(tested_index) == true); tested_index = monomer_text.size(); REQUIRE(sequence_1.isInBound(tested_index) == false); } } } SCENARIO("Monomers can be added or removed from a Sequence", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A newly allocated sequence with a string sequence during construction") { Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); QString monomer_text = sequence_1.getSequence(); REQUIRE( monomer_text.toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter.toStdString()); REQUIRE(monomer_text.size() == test_utils_sequence_1_letter.m_telokinSequenceMonomerCount); REQUIRE(static_cast(monomer_text.size()) == sequence_1.size()); WHEN("A Monomer object is inserted at the beginining of the sequence") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "W"); sequence_1.insertMonomerAt(monomer, 0); THEN("That monomer has to be found there") { REQUIRE(sequence_1.getMonomerCstSPtrAt(0)->getCode().toStdString() == "W"); QString w_monomer_text = monomer_text; w_monomer_text.prepend("W"); REQUIRE(sequence_1.getSequence().toStdString() == w_monomer_text.toStdString()); } AND_WHEN("When searching for a motif comprising that monomer") { Sequence motif_sequence_1_occurrence(pol_chem_def_csp, "WMAMISGMSGR"); std::size_t search_index_1 = 0; int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_1); THEN("The search is successful") { REQUIRE(result_1 == true); REQUIRE(search_index_1 == 0); } } } WHEN("A monomer is inserted at the end of the sequence") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "W"); // qDebug() << "The size of the sequence:" << sequence_1.size() // << "The count of monomers:" // << test_utils_sequence_1_letter.m_telokinSequenceMonomerCount; sequence_1.insertMonomerAt( monomer, test_utils_sequence_1_letter.m_telokinSequenceMonomerCount); THEN("That monomer has to be found there") { REQUIRE(sequence_1 .getMonomerCstSPtrAt( test_utils_sequence_1_letter.m_telokinSequenceMonomerCount) ->getCode() .toStdString() == "W"); } AND_WHEN("When searching for a motif comprising that monomer") { Sequence motif_sequence_1_occurrence(pol_chem_def_csp, "EEEEEEW"); std::size_t search_index_1 = 0; int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_1); THEN("The search is successful") { REQUIRE(result_1 == true); REQUIRE(search_index_1 == 151); } } } WHEN("A monomer is inserted in the middle of the sequence") { Monomer monomer(pol_chem_def_csp, "Tryptophan", "W"); int floor = std::floor(sequence_1.size() / 2); // qDebug() << "The floor:" << floor; sequence_1.insertMonomerAt(monomer, floor); THEN("That monomer has to be found there") { REQUIRE( sequence_1.getMonomerCstSPtrAt(floor)->getCode().toStdString() == "W"); } AND_WHEN("When searching for a motif comprising that monomer") { Sequence motif_sequence_1_occurrence(pol_chem_def_csp, "DPEVMWWYKDDQ"); std::size_t search_index_1 = 0; int result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_1); THEN("The search is successful") { REQUIRE(result_1 == true); REQUIRE(search_index_1 == 72); } AND_WHEN("That monomer is removed back") { sequence_1.removeMonomerAt(floor); THEN("The sequence with the inserted monomer cannot be found anymore") { search_index_1 = 0; result_1 = sequence_1.findForwardMotif(motif_sequence_1_occurrence, search_index_1); REQUIRE(result_1 == 0); REQUIRE(search_index_1 == 0); AND_THEN("The original sequence can be found again") { std::vector failing_indices; motif_sequence_1_occurrence.setSequence("DPEVMWYKDDQ", failing_indices); search_index_1 = 0; result_1 = sequence_1.findForwardMotif( motif_sequence_1_occurrence, search_index_1); REQUIRE(result_1 == 1); REQUIRE(search_index_1 == 72); } } AND_WHEN("An empty monomer sequence text is appended to the Sequence") { Sequence sequence_2 = sequence_1; std::vector failing_indices; sequence_1.appendSequence("", failing_indices); THEN("The sequence should remain unchanged") { REQUIRE(sequence_1.size() == sequence_2.size()); REQUIRE(sequence_1 == sequence_2); } AND_WHEN("A monomer sequence text is appended to the Sequence") { THEN("The original sequence can be found again") { // qDebug() << "The sequence:" << sequence_1.getSequence(70, // 85); std::vector failing_indices; motif_sequence_1_occurrence.setSequence("DPEVMWYKDDQ", failing_indices); search_index_1 = 0; result_1 = sequence_1.findForwardMotif( motif_sequence_1_occurrence, search_index_1); // qDebug() << "The index at which the motif was found:" // << search_index_1; REQUIRE(result_1 == 1); REQUIRE(search_index_1 == 72); } REQUIRE(sequence_1.getSequence().toStdString() == test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter .toStdString()); std::vector failing_indices; sequence_1.appendSequence("THISTEQST", failing_indices); THEN("Searching for it should return the right index") { Sequence motif_sequence_1_occurrence(pol_chem_def_csp, "EEEEEETHISTEQST"); std::size_t search_index_1 = 0; int result_1 = sequence_1.findForwardMotif( motif_sequence_1_occurrence, search_index_1); REQUIRE(result_1 == true); REQUIRE(search_index_1 == 151); } } } } } } } } SCENARIO( "Ranges of Monomer instances can be extracted in various ways from a " "Sequence", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; GIVEN("A newly allocated sequence with a string sequence during construction") { Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); QString monomer_text = sequence_1.getSequence(); REQUIRE(monomer_text.size() == test_utils_sequence_1_letter.m_telokinSequenceMonomerCount); REQUIRE(static_cast(monomer_text.size()) == sequence_1.size()); IndexRange index_range(0, 12); IndexRangeCollection index_range_collection; index_range_collection.appendIndexRange(index_range); WHEN("Trying to get an index-based Monomer text") { QString text = sequence_1.getSequence(0, 12, /*with_modif*/ false); THEN("The returned string must the right size and sequence") { REQUIRE(text.size() == 13); REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } WHEN("Trying to get a sequence ranged-based Monomer text") { QString text = sequence_1.getSequence(index_range_collection, /*with_modif*/ false, /*delimited_regions*/ false); THEN("The returned string must the right size and sequence") { REQUIRE(text.size() == 13); REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } WHEN( "Modifying one monomer in the Sequence (using std::const_pointer_cast)") { std::unique_ptr phospho_up = std::make_unique(pol_chem_def_csp, "Phosphorylation", "H1O3P1"); phospho_up->setTargets("S"); phospho_up->setMaxCount(1); MonomerSPtr serine_7_csp = sequence_1.getMonomerCstSPtrAt(7); // qDebug() << "The monomer at index 7:" << serine_7_csp->toString(); REQUIRE(serine_7_csp->getCode().toStdString() == "S"); REQUIRE(serine_7_csp->isModifTarget(*phospho_up.get()) == true); // Need to cast away constness of the MonomerCstSPtr. MonomerSPtr serine_7_sp = std::const_pointer_cast(serine_7_csp); REQUIRE(serine_7_sp->modify(*phospho_up.get(), /* override modif count */ false, error_list_sequence) != nullptr); AND_WHEN( "Asking for the monomer index-based range without specifying that " "modifs are requested") { QString text = sequence_1.getSequence(0, 12, /*with_modif*/ false); THEN("The returned string must match the peptidic Monomer text") { REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } AND_WHEN( "Asking for the monomer coordinates-based range without specifying " "that modifs are requested") { QString text = sequence_1.getSequence(index_range_collection, /*with_modif*/ false, /*delimited_regions*/ false); THEN("The returned string must match the peptidic Monomer text") { REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } AND_WHEN( "Asking for the monomer index-based range with specifying that modifs " "are requested") { QString text = sequence_1.getSequence(0, 12, /*with_modif*/ true); THEN("The returned string must match the peptidic Monomer text") { REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } AND_WHEN( "Asking for the monomer coordinates-based range with specifying that " "modifs " "are " "requested") { QString text = sequence_1.getSequence(index_range_collection, /*with_modif*/ true, /*delimited_regions*/ false); THEN("The returned string must match the peptidic Monomer text") { REQUIRE(text.toStdString() == "MAMISGMSGRKAS"); } } AND_WHEN( "Asking for the monomer coordinates-based range with specifying that " "modifs " "are " "requested and with delimited regions") { QString text = sequence_1.getSequence(index_range_collection, /*with_modif*/ true, /*delimited_regions*/ true); THEN("The returned string must match the peptidic Monomer text") { REQUIRE(text.toStdString() == "Region [1-13]: MAMISGMSGRKAS\n"); } } } } } SCENARIO("A sequence can compute a checksum", "[Sequence]") { PolChemDefCstSPtr pol_chem_def_csp = test_utils_sequence_1_letter.msp_polChemDef; Sequence sequence_1( pol_chem_def_csp, test_utils_sequence_1_letter.m_telokinAsMonomerText1Letter); quint16 check_sum_no_modifs = 0; quint16 check_sum_modifs = 0; GIVEN( "A newly allocated non-modified sequence with a string sequence during " "construction") { WHEN("Computing the checksum") { qDebug() << "The sequence size is:"<< sequence_1.getSequence().size(); quint16 check_sum = sequence_1.checksum(0, sequence_1.getSequence().size() - 1); THEN("The returned check sum is not zero") { REQUIRE(check_sum != 0); } } WHEN("Computing the checksum without modifs") { check_sum_no_modifs = sequence_1.checksum( 0, sequence_1.getSequence().size() - 1, /*with_modifs*/ false); AND_WHEN("Computing the checksum with the modifs") { check_sum_modifs = sequence_1.checksum( 0, sequence_1.getSequence().size() - 1, /*with_modifs*/ true); THEN( "Both check sums should be identical because the sequence is not " "modified") { REQUIRE(check_sum_no_modifs == check_sum_modifs); } AND_WHEN("The sequence is phosphorylated on a serine") { std::unique_ptr phospho_up = std::make_unique( pol_chem_def_csp, "Phosphorylation", "H1O3P1"); phospho_up->setTargets("S"); phospho_up->setMaxCount(1); MonomerSPtr serine_7_csp = sequence_1.getMonomerCstSPtrAt(7); REQUIRE(serine_7_csp->getCode().toStdString() == "S"); REQUIRE(serine_7_csp->isModifTarget(*phospho_up.get()) == true); // Need to cast away constness of the MonomerCstSPtr. MonomerSPtr serine_7_sp = std::const_pointer_cast(serine_7_csp); REQUIRE(serine_7_sp->modify(*phospho_up.get(), /* override modif count */ false, error_list_sequence) != nullptr); THEN( "The new checksum with modifs must be different than the one " "calculated before") { quint16 new_check_sum = sequence_1.checksum( 0, sequence_1.getSequence().size() - 1, /*with_modifs*/ true); REQUIRE(new_check_sum != 0); REQUIRE(new_check_sum != check_sum_modifs); } } } } } } } // namespace libXpertMassCore } // namespace MsXpS libxpertmass-1.4.0/tests/test_SequenceRanges.cpp000664 001750 001750 00000062457 15100504560 023270 0ustar00rusconirusconi000000 000000 // ./tests/catch2-tests [section] -s /////////////////////// Qt includes #include #include #include /////////////////////// Catch2 includes #include #include /////////////////////// Local includes #include "TestUtils.hpp" #include namespace MsXpS { namespace libXpertMassCore { SCENARIO("Construction of a SequenceRanges instance", "[SequenceRanges]") { QString sequence_range_string_1("[50-150]"); SequenceRange sequence_range_1(50, 150); QString sequence_range_string_2("[40-160]"); SequenceRange sequence_range_2(40, 160); QString sequence_range_string_3("[30-170]"); SequenceRange sequence_range_3(30, 170); QString all_three_sequence_range_strings = QString("%1%2%3") .arg(sequence_range_string_1) .arg(sequence_range_string_2) .arg(sequence_range_string_3); GIVEN("Construction with no argument") { SequenceRanges sequence_ranges; WHEN("A comment is set to it") { sequence_ranges.setComment("Comment"); THEN("The comment should be set") { REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); } } } GIVEN( "Construction of a SequenceRanges instance with a string representing " "three SequenceRange instances") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list must have size 3") { REQUIRE(sequence_ranges.size() == 3); AND_THEN("All the members of the list should have proper values") { SequenceRange first_sequence_range = sequence_ranges.getRangeCstRefAt(0); REQUIRE(first_sequence_range.start == sequence_range_1.start - 1); REQUIRE(first_sequence_range.stop == sequence_range_1.stop - 1); SequenceRange second_sequence_range = sequence_ranges.getRangeCstRefAt(1); REQUIRE(second_sequence_range.start == sequence_range_2.start - 1); REQUIRE(second_sequence_range.stop == sequence_range_2.stop - 1); SequenceRange third_sequence_range = sequence_ranges.getRangeCstRefAt(2); REQUIRE(third_sequence_range.start == sequence_range_3.start - 1); REQUIRE(third_sequence_range.stop == sequence_range_3.stop - 1); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "'[')") { QString all_three_sequence_range_strings("50-150][40-160][30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "']'") { QString all_three_sequence_range_strings("[50-150][40-160[30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "']' and 1 '['") { QString all_three_sequence_range_strings("50-150][40-160[30-170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN( "An incorrect string incorrectly representing three SequenceRange (missing " "1 " "'-'") { QString all_three_sequence_range_strings("[50-150][40-160][30 170]"); WHEN("Constructing a SequenceRanges instance") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list is created empty") { REQUIRE(sequence_ranges.size() == 0); } } } GIVEN("Construction with a comment and no list; and SequenceRange objects") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Comment"); REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); REQUIRE(sequence_ranges.size() == 0); WHEN("One SequenceRange object is appended to the list") { sequence_ranges.appendSequenceRange(sequence_range_1); THEN( "The list must have size 1, and that object has to be the one appended") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0) == sequence_range_1); } AND_WHEN("Another SequenceRange object is appended to the list") { sequence_ranges.appendSequenceRange(sequence_range_2); THEN( "The list must have size 2, and that object has to be the last of " "the list") { REQUIRE(sequence_ranges.size() == 2); REQUIRE(sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1) == sequence_range_2); } AND_WHEN( "That 2-sized SequenceRanges instance is copied (copy-constructor " "with reference)") { SequenceRanges new_sequence_ranges(sequence_ranges); THEN("The two SequenceRanges instances must have the same data") { REQUIRE(new_sequence_ranges.getComment().toStdString() == sequence_ranges.getComment().toStdString()); REQUIRE(new_sequence_ranges.size() == sequence_ranges.size()); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0) == sequence_ranges.getRangeCstRefAt(0)); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) == sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1)); } } AND_WHEN( "That 2-sized SequenceRanges instance is copied (assignment " "operator) to itself, a reference to itself is returned") { REQUIRE((sequence_ranges = sequence_ranges) == sequence_ranges); } AND_WHEN( "That 2-sized SequenceRanges instance is copied (assignment " "operator)") { SequenceRanges new_sequence_ranges; new_sequence_ranges = sequence_ranges; THEN("The two SequenceRanges instances must have the same data") { REQUIRE(new_sequence_ranges.getComment().toStdString() == sequence_ranges.getComment().toStdString()); REQUIRE(new_sequence_ranges.size() == sequence_ranges.size()); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0) == sequence_ranges.getRangeCstRefAt(0)); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) == sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1)); } } } } } } SCENARIO( "Empty SequenceRanges instances can be filled-in after construction with " "other SequenceRanges", "[SequenceRanges]") { GIVEN( "Construction of a SequenceRanges instance with two SequenceRange " "instances") { SequenceRange sequence_range_1(50, 150); SequenceRange sequence_range_2(40, 160); SequenceRange sequence_range_3(30, 170); SequenceRanges sequence_ranges_0; sequence_ranges_0.setComment("Giver"); sequence_ranges_0.appendSequenceRange(sequence_range_1); sequence_ranges_0.appendSequenceRange(sequence_range_2); REQUIRE(sequence_ranges_0.size() == 2); REQUIRE(sequence_ranges_0.getRangeCstRefAt(0) == sequence_range_1); REQUIRE(sequence_ranges_0.getRangeCstRefAt(sequence_ranges_0.size() - 1) == sequence_range_2); AND_GIVEN("Construction of an empty SequenceRanges instance") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Receiver"); REQUIRE(sequence_ranges.getComment().toStdString() == "Receiver"); REQUIRE(sequence_ranges.size() == 0); WHEN( "The first SequenceRanges instance with two items is appended to the " "the empty one") { sequence_ranges.appendSequenceRanges(sequence_ranges_0); THEN("Its size must become the same at the one that was appended") { REQUIRE(sequence_ranges.size() == sequence_ranges_0.size()); REQUIRE(sequence_ranges.getRangeCstRefAt(0) == sequence_ranges_0.getRangeCstRefAt(0)); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1) == sequence_ranges_0.getRangeCstRefAt(sequence_ranges_0.size() - 1)); } AND_WHEN("A new SequenceRange instance is set (not appended)") { sequence_ranges.setSequenceRange(sequence_range_3); THEN("The single instance has to be this last") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0) == sequence_range_3); } AND_WHEN("A SequenceRanges instance is set (not appended)") { sequence_ranges.setSequenceRanges(sequence_ranges_0); THEN( "The previous single instance has to be lost and be replaced by " "the new ones") { REQUIRE(sequence_ranges.size() == 2); REQUIRE(sequence_ranges.getRangeCstRefAt(0) == sequence_ranges_0.getRangeCstRefAt(0)); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1) == sequence_ranges_0.getRangeCstRefAt(sequence_ranges_0.size() - 1)); } } } } } } } SCENARIO( "Empty SequenceRanges instances can be filled in after construction with " "strings representative of SequenceRange instances (single or multiple)", "[SequenceRanges]") { QString sequence_range_string_1("[50-150]"); SequenceRange sequence_range_1(50, 150); QString sequence_range_string_2("[40-160]"); SequenceRange sequence_range_2(40, 160); QString sequence_range_string_3("[30-170]"); SequenceRange sequence_range_3(30, 170); QString all_three_sequence_range_strings = QString("%1%2%3") .arg(sequence_range_string_1) .arg(sequence_range_string_2) .arg(sequence_range_string_3); GIVEN("Construction with a comment and no list; and SequenceRange objects") { SequenceRanges sequence_ranges; sequence_ranges.setComment("Comment"); REQUIRE(sequence_ranges.getComment().toStdString() == "Comment"); REQUIRE(sequence_ranges.size() == 0); WHEN( "That SequenceRanges is set with one string representative of a single " "SequenceRange instance") { sequence_ranges.setSequenceRanges(sequence_range_string_1); THEN( "The list must have size 1, and that object has to be the one set, " "with each first/second value decremented by one to convert positions " "to " "indices") { REQUIRE(sequence_ranges.size() == 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0).start == sequence_range_1.start - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0).stop == sequence_range_1.stop - 1); } } WHEN( "That SequenceRanges is set with one string representative of three " "SequenceRange instances") { sequence_ranges.setSequenceRanges(all_three_sequence_range_strings); THEN("The list must have size 3") { REQUIRE(sequence_ranges.size() == 3); } AND_THEN("All the members of the list should have proper values") { REQUIRE(sequence_ranges.getRangeCstRefAt(0).start == sequence_range_1.start - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0).stop == sequence_range_1.stop - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(1).start == sequence_range_2.start - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(1).stop == sequence_range_2.stop - 1); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1).start == sequence_range_3.start - 1); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1).stop == sequence_range_3.stop - 1); } } } } SCENARIO("SequenceRanges instances can provide topological data", "[SequenceRanges]") { GIVEN("A SequenceRanges instance with a single SequenceRange instance") { SequenceRanges sequence_ranges; QString all_sequence_range_strings("[1-20]"); sequence_ranges.setSequenceRanges(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 1); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRange instances") { REQUIRE(sequence_ranges.leftMostSequenceRanges(left_most_indices) == 1); REQUIRE(sequence_ranges.rightMostSequenceRanges(right_most_indices) == 1); THEN( "The indices should actually match the correct SequenceRange instance") { REQUIRE( sequence_ranges.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( sequence_ranges.getRangeCstRefAt(right_most_indices.at(0)).stop == 19); } } WHEN( "Checking if there are overlaps, there cannot be because only one " "instance") { REQUIRE_FALSE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE(sequence_ranges.encompassIndex(19)); REQUIRE(sequence_ranges.encompassIndex(10)); REQUIRE_FALSE(sequence_ranges.encompassIndex(20)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); } } } GIVEN( "A SequenceRanges instance with a number of non-overlapping SequenceRange") { SequenceRanges sequence_ranges; QString all_sequence_range_strings("[1-20][25-45][55-100]"); sequence_ranges.setSequenceRanges(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 3); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRanges") { REQUIRE(sequence_ranges.leftMostSequenceRanges(left_most_indices) == 1); REQUIRE(sequence_ranges.rightMostSequenceRanges(right_most_indices) == 1); THEN( "The indices should actually match the right SequenceRange instances") { REQUIRE( sequence_ranges.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( sequence_ranges.getRangeCstRefAt(right_most_indices.at(0)).stop == 99); } } WHEN("Checking if there are overlaps, there should not be") { REQUIRE_FALSE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE_FALSE(sequence_ranges.encompassIndex(50)); REQUIRE(sequence_ranges.encompassIndex(50, /*globally*/ true)); REQUIRE(sequence_ranges.encompassIndex(99)); REQUIRE_FALSE(sequence_ranges.encompassIndex(100)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { // "[1-20][25-45][55-100]" REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(1))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(1))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(2))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(2))); } } } GIVEN( "A SequenceRanges instance with a number of SequenceRange with overlaps") { SequenceRanges sequence_ranges; QString all_sequence_range_strings( "[1-20][1-30][10-50][15-80][25-100][30-100]"); sequence_ranges.setSequenceRanges(all_sequence_range_strings); REQUIRE(sequence_ranges.size() == 6); std::vector left_most_indices; std::vector right_most_indices; WHEN("Asking for the leftmost and the rightmost SequenceRanges") { REQUIRE(sequence_ranges.leftMostSequenceRanges(left_most_indices) == 2); REQUIRE(sequence_ranges.rightMostSequenceRanges(right_most_indices) == 2); THEN( "The indices should actually match the right SequenceRange instances") { REQUIRE( sequence_ranges.getRangeCstRefAt(left_most_indices.at(0)).start == 0); REQUIRE( sequence_ranges.getRangeCstRefAt(left_most_indices.back()).start == 0); REQUIRE( sequence_ranges.getRangeCstRefAt(right_most_indices.at(0)).stop == 99); REQUIRE( sequence_ranges.getRangeCstRefAt(right_most_indices.back()).stop == 99); } } WHEN("Checking if there are overlaps, there should be") { REQUIRE(sequence_ranges.overlap()); } WHEN("Checking for indices that are encompassed or not") { THEN("The results should be consistent") { REQUIRE(sequence_ranges.encompassIndex(0)); REQUIRE(sequence_ranges.encompassIndex(49)); REQUIRE(sequence_ranges.encompassIndex(50)); REQUIRE(sequence_ranges.encompassIndex(99)); REQUIRE_FALSE(sequence_ranges.encompassIndex(100)); } } WHEN("Checking for indices left most SequenceRange") { THEN("The results should be consistent") { // "[1-20][1-30][10-50][15-80][25-100][30-100]" REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(0))); REQUIRE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(1))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(1))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(2))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(2))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(3))); REQUIRE_FALSE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(3))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(4))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(4))); REQUIRE_FALSE(sequence_ranges.isLeftMostSequenceRange( sequence_ranges.getRangeCstRefAt(5))); REQUIRE(sequence_ranges.isRightMostSequenceRange( sequence_ranges.getRangeCstRefAt(5))); } } } } SCENARIO("SequenceRanges instances can self-describe with text strings", "[SequenceRanges]") { QString all_three_sequence_range_strings("[50-150][40-160][30-170]"); GIVEN("Construction with a string describing three SequenceRange instances") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); WHEN("When asked to self-describe as indices") { QString text = sequence_ranges.indicesAsText(); THEN("The string should be list indices and not positions") { REQUIRE(text.toStdString() == "[49-149][39-159][29-169]"); } } WHEN("When asked to self-describe as positions") { QString text = sequence_ranges.positionsAsText(); THEN("The string should be list positions and not indices") { REQUIRE(text.toStdString() == all_three_sequence_range_strings.toStdString()); } } } } SCENARIO("SequenceRanges and SequenceRange instances can be compared", "[SequenceRanges]") { QString sequence_range_string_1("[50-150]"); SequenceRange sequence_range_1(50, 150); QString sequence_range_string_2("[40-160]"); SequenceRange sequence_range_2(40, 160); QString sequence_range_string_3("[30-170]"); SequenceRange sequence_range_3(30, 170); QString all_three_sequence_range_strings = QString("%1%2%3") .arg(sequence_range_string_1) .arg(sequence_range_string_2) .arg(sequence_range_string_3); GIVEN("A SequenceRanges instance with 3 SequenceRange instances") { SequenceRanges sequence_ranges(all_three_sequence_range_strings); THEN("The list must have size 3") { REQUIRE(sequence_ranges.size() == 3); } AND_THEN("All the members of the list should have proper values") { REQUIRE(sequence_ranges.getRangeCstRefAt(0).start == sequence_range_1.start - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(0).stop == sequence_range_1.stop - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(1).start == sequence_range_2.start - 1); REQUIRE(sequence_ranges.getRangeCstRefAt(1).stop == sequence_range_2.stop - 1); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1).start == sequence_range_3.start - 1); REQUIRE( sequence_ranges.getRangeCstRefAt(sequence_ranges.size() - 1).stop == sequence_range_3.stop - 1); } WHEN("A new SequenceRanges instance is created as a copy of the first one") { SequenceRanges new_sequence_ranges(sequence_ranges); THEN("The new instance should be identical to the initial one") { REQUIRE(new_sequence_ranges.size() == 3); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0).start == sequence_range_1.start - 1); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0).stop == sequence_range_1.stop - 1); REQUIRE(new_sequence_ranges.getRangeCstRefAt(1).start == sequence_range_2.start - 1); REQUIRE(new_sequence_ranges.getRangeCstRefAt(1).stop == sequence_range_2.stop - 1); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) .start == sequence_range_3.start - 1); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) .stop == sequence_range_3.stop - 1); } AND_THEN("The comparison operators should return identity") { REQUIRE(new_sequence_ranges == sequence_ranges); REQUIRE_FALSE(new_sequence_ranges != sequence_ranges); } AND_WHEN("A one SequenceRange instance is modified") { // SequenceRange sequence_range_1(50, 150); // SequenceRange sequence_range_2(40, 160); // SequenceRange sequence_range_3(30, 170); SequenceRanges new_sequence_ranges(sequence_ranges); SequenceRange &sequence_range = new_sequence_ranges.getRangeRefAt(1); sequence_range.start = 50; sequence_range.stop = 150; THEN("The new instance should not be identical anymore") { REQUIRE(new_sequence_ranges.size() == 3); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0).start == sequence_range_1.start - 1); REQUIRE(new_sequence_ranges.getRangeCstRefAt(0).stop == sequence_range_1.stop - 1); REQUIRE(new_sequence_ranges.getRangeCstRefAt(1).start == 50); REQUIRE(new_sequence_ranges.getRangeCstRefAt(1).stop == 150); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) .start == sequence_range_3.start - 1); REQUIRE( new_sequence_ranges.getRangeCstRefAt(new_sequence_ranges.size() - 1) .stop == sequence_range_3.stop - 1); } AND_THEN("The comparison operators should return identity") { REQUIRE(new_sequence_ranges != sequence_ranges); REQUIRE_FALSE(new_sequence_ranges == sequence_ranges); } } } } } } // namespace libXpertMassCore } // namespace MsXpS